mirror of https://github.com/vector-im/riot-web
				
				
				
			Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/17368
						commit
						24b03366a8
					
				| 
						 | 
				
			
			@ -123,6 +123,7 @@
 | 
			
		|||
    "@sinonjs/fake-timers": "^7.0.2",
 | 
			
		||||
    "@types/classnames": "^2.2.11",
 | 
			
		||||
    "@types/counterpart": "^0.18.1",
 | 
			
		||||
    "@types/diff-match-patch": "^1.0.5",
 | 
			
		||||
    "@types/flux": "^3.1.9",
 | 
			
		||||
    "@types/jest": "^26.0.20",
 | 
			
		||||
    "@types/linkifyjs": "^2.1.3",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/*
 | 
			
		||||
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.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
declare module "diff-dom" {
 | 
			
		||||
    enum Action {
 | 
			
		||||
        AddElement = "addElement",
 | 
			
		||||
        AddTextElement = "addTextElement",
 | 
			
		||||
        RemoveTextElement = "removeTextElement",
 | 
			
		||||
        RemoveElement = "removeElement",
 | 
			
		||||
        ReplaceElement = "replaceElement",
 | 
			
		||||
        ModifyTextElement = "modifyTextElement",
 | 
			
		||||
        AddAttribute = "addAttribute",
 | 
			
		||||
        RemoveAttribute = "removeAttribute",
 | 
			
		||||
        ModifyAttribute = "modifyAttribute",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export interface IDiff {
 | 
			
		||||
        action: Action;
 | 
			
		||||
        name: string;
 | 
			
		||||
        text?: string;
 | 
			
		||||
        route: number[];
 | 
			
		||||
        value: string;
 | 
			
		||||
        element: unknown;
 | 
			
		||||
        oldValue: string;
 | 
			
		||||
        newValue: string;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface IOpts {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export class DiffDOM {
 | 
			
		||||
        public constructor(opts?: IOpts);
 | 
			
		||||
        public apply(tree: unknown, diffs: IDiff[]): unknown;
 | 
			
		||||
        public undo(tree: unknown, diffs: IDiff[]): unknown;
 | 
			
		||||
        public diff(a: HTMLElement | string, b: HTMLElement | string): IDiff[];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2018 New Vector Ltd
 | 
			
		||||
Copyright 2018 - 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,34 +14,40 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
 | 
			
		||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
 | 
			
		||||
export class DecryptionFailure {
 | 
			
		||||
    constructor(failedEventId, errorCode) {
 | 
			
		||||
        this.failedEventId = failedEventId;
 | 
			
		||||
        this.errorCode = errorCode;
 | 
			
		||||
    public readonly ts: number;
 | 
			
		||||
 | 
			
		||||
    constructor(public readonly failedEventId: string, public readonly errorCode: string) {
 | 
			
		||||
        this.ts = Date.now();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TrackingFn = (count: number, trackedErrCode: string) => void;
 | 
			
		||||
type ErrCodeMapFn = (errcode: string) => string;
 | 
			
		||||
 | 
			
		||||
export class DecryptionFailureTracker {
 | 
			
		||||
    // Array of items of type DecryptionFailure. Every `CHECK_INTERVAL_MS`, this list
 | 
			
		||||
    // is checked for failures that happened > `GRACE_PERIOD_MS` ago. Those that did
 | 
			
		||||
    // are accumulated in `failureCounts`.
 | 
			
		||||
    failures = [];
 | 
			
		||||
    public failures: DecryptionFailure[] = [];
 | 
			
		||||
 | 
			
		||||
    // A histogram of the number of failures that will be tracked at the next tracking
 | 
			
		||||
    // interval, split by failure error code.
 | 
			
		||||
    failureCounts = {
 | 
			
		||||
    public failureCounts: Record<string, number> = {
 | 
			
		||||
        // [errorCode]: 42
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Event IDs of failures that were tracked previously
 | 
			
		||||
    trackedEventHashMap = {
 | 
			
		||||
    public trackedEventHashMap: Record<string, boolean> = {
 | 
			
		||||
        // [eventId]: true
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Set to an interval ID when `start` is called
 | 
			
		||||
    checkInterval = null;
 | 
			
		||||
    trackInterval = null;
 | 
			
		||||
    public checkInterval: NodeJS.Timeout = null;
 | 
			
		||||
    public trackInterval: NodeJS.Timeout = null;
 | 
			
		||||
 | 
			
		||||
    // Spread the load on `Analytics` by tracking at a low frequency, `TRACK_INTERVAL_MS`.
 | 
			
		||||
    static TRACK_INTERVAL_MS = 60000;
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +73,7 @@ export class DecryptionFailureTracker {
 | 
			
		|||
     * @param {function?} errorCodeMapFn The function used to map error codes to the
 | 
			
		||||
     * trackedErrorCode. If not provided, the `.code` of errors will be used.
 | 
			
		||||
     */
 | 
			
		||||
    constructor(fn, errorCodeMapFn) {
 | 
			
		||||
    constructor(private readonly fn: TrackingFn, private readonly errorCodeMapFn?: ErrCodeMapFn) {
 | 
			
		||||
        if (!fn || typeof fn !== 'function') {
 | 
			
		||||
            throw new Error('DecryptionFailureTracker requires tracking function');
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -75,9 +81,6 @@ export class DecryptionFailureTracker {
 | 
			
		|||
        if (errorCodeMapFn && typeof errorCodeMapFn !== 'function') {
 | 
			
		||||
            throw new Error('DecryptionFailureTracker second constructor argument should be a function');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._trackDecryptionFailure = fn;
 | 
			
		||||
        this._mapErrorCode = errorCodeMapFn;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // loadTrackedEventHashMap() {
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +91,7 @@ export class DecryptionFailureTracker {
 | 
			
		|||
    //     localStorage.setItem('mx-decryption-failure-event-id-hashes', JSON.stringify(this.trackedEventHashMap));
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    eventDecrypted(e, err) {
 | 
			
		||||
    public eventDecrypted(e: MatrixEvent, err: MatrixError | Error): void {
 | 
			
		||||
        if (err) {
 | 
			
		||||
            this.addDecryptionFailure(new DecryptionFailure(e.getId(), err.code));
 | 
			
		||||
        } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -97,18 +100,18 @@ export class DecryptionFailureTracker {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addDecryptionFailure(failure) {
 | 
			
		||||
    public addDecryptionFailure(failure: DecryptionFailure): void {
 | 
			
		||||
        this.failures.push(failure);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeDecryptionFailuresForEvent(e) {
 | 
			
		||||
    public removeDecryptionFailuresForEvent(e: MatrixEvent): void {
 | 
			
		||||
        this.failures = this.failures.filter((f) => f.failedEventId !== e.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start checking for and tracking failures.
 | 
			
		||||
     */
 | 
			
		||||
    start() {
 | 
			
		||||
    public start(): void {
 | 
			
		||||
        this.checkInterval = setInterval(
 | 
			
		||||
            () => this.checkFailures(Date.now()),
 | 
			
		||||
            DecryptionFailureTracker.CHECK_INTERVAL_MS,
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +126,7 @@ export class DecryptionFailureTracker {
 | 
			
		|||
    /**
 | 
			
		||||
     * Clear state and stop checking for and tracking failures.
 | 
			
		||||
     */
 | 
			
		||||
    stop() {
 | 
			
		||||
    public stop(): void {
 | 
			
		||||
        clearInterval(this.checkInterval);
 | 
			
		||||
        clearInterval(this.trackInterval);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -132,11 +135,11 @@ export class DecryptionFailureTracker {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark failures that occured before nowTs - GRACE_PERIOD_MS as failures that should be
 | 
			
		||||
     * Mark failures that occurred before nowTs - GRACE_PERIOD_MS as failures that should be
 | 
			
		||||
     * tracked. Only mark one failure per event ID.
 | 
			
		||||
     * @param {number} nowTs the timestamp that represents the time now.
 | 
			
		||||
     */
 | 
			
		||||
    checkFailures(nowTs) {
 | 
			
		||||
    public checkFailures(nowTs: number): void {
 | 
			
		||||
        const failuresGivenGrace = [];
 | 
			
		||||
        const failuresNotReady = [];
 | 
			
		||||
        while (this.failures.length > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -175,10 +178,10 @@ export class DecryptionFailureTracker {
 | 
			
		|||
 | 
			
		||||
        const dedupedFailures = dedupedFailuresMap.values();
 | 
			
		||||
 | 
			
		||||
        this._aggregateFailures(dedupedFailures);
 | 
			
		||||
        this.aggregateFailures(dedupedFailures);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _aggregateFailures(failures) {
 | 
			
		||||
    private aggregateFailures(failures: DecryptionFailure[]): void {
 | 
			
		||||
        for (const failure of failures) {
 | 
			
		||||
            const errorCode = failure.errorCode;
 | 
			
		||||
            this.failureCounts[errorCode] = (this.failureCounts[errorCode] || 0) + 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -189,12 +192,12 @@ export class DecryptionFailureTracker {
 | 
			
		|||
     * If there are failures that should be tracked, call the given trackDecryptionFailure
 | 
			
		||||
     * function with the number of failures that should be tracked.
 | 
			
		||||
     */
 | 
			
		||||
    trackFailures() {
 | 
			
		||||
    public trackFailures(): void {
 | 
			
		||||
        for (const errorCode of Object.keys(this.failureCounts)) {
 | 
			
		||||
            if (this.failureCounts[errorCode] > 0) {
 | 
			
		||||
                const trackedErrorCode = this._mapErrorCode ? this._mapErrorCode(errorCode) : errorCode;
 | 
			
		||||
                const trackedErrorCode = this.errorCodeMapFn ? this.errorCodeMapFn(errorCode) : errorCode;
 | 
			
		||||
 | 
			
		||||
                this._trackDecryptionFailure(this.failureCounts[errorCode], trackedErrorCode);
 | 
			
		||||
                this.fn(this.failureCounts[errorCode], trackedErrorCode);
 | 
			
		||||
                this.failureCounts[errorCode] = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -17,11 +17,10 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { ReactNode } from 'react';
 | 
			
		||||
import sanitizeHtml from 'sanitize-html';
 | 
			
		||||
import { IExtendedSanitizeOptions } from './@types/sanitize-html';
 | 
			
		||||
import cheerio from 'cheerio';
 | 
			
		||||
import * as linkify from 'linkifyjs';
 | 
			
		||||
import linkifyMatrix from './linkify-matrix';
 | 
			
		||||
import _linkifyElement from 'linkifyjs/element';
 | 
			
		||||
import _linkifyString from 'linkifyjs/string';
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
| 
						 | 
				
			
			@ -29,13 +28,15 @@ import EMOJIBASE_REGEX from 'emojibase-regex';
 | 
			
		|||
import url from 'url';
 | 
			
		||||
import katex from 'katex';
 | 
			
		||||
import { AllHtmlEntities } from 'html-entities';
 | 
			
		||||
import SettingsStore from './settings/SettingsStore';
 | 
			
		||||
import cheerio from 'cheerio';
 | 
			
		||||
import { IContent } from 'matrix-js-sdk/src/models/event';
 | 
			
		||||
 | 
			
		||||
import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks";
 | 
			
		||||
import {SHORTCODE_TO_EMOJI, getEmojiFromUnicode} from "./emoji";
 | 
			
		||||
import { IExtendedSanitizeOptions } from './@types/sanitize-html';
 | 
			
		||||
import linkifyMatrix from './linkify-matrix';
 | 
			
		||||
import SettingsStore from './settings/SettingsStore';
 | 
			
		||||
import { tryTransformPermalinkToLocalHref } from "./utils/permalinks/Permalinks";
 | 
			
		||||
import { SHORTCODE_TO_EMOJI, getEmojiFromUnicode } from "./emoji";
 | 
			
		||||
import ReplyThread from "./components/views/elements/ReplyThread";
 | 
			
		||||
import {mediaFromMxc} from "./customisations/Media";
 | 
			
		||||
import { mediaFromMxc } from "./customisations/Media";
 | 
			
		||||
 | 
			
		||||
linkifyMatrix(linkify);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +67,7 @@ export const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet'
 | 
			
		|||
 * need emojification.
 | 
			
		||||
 * unicodeToImage uses this function.
 | 
			
		||||
 */
 | 
			
		||||
function mightContainEmoji(str: string) {
 | 
			
		||||
function mightContainEmoji(str: string): boolean {
 | 
			
		||||
    return SURROGATE_PAIR_PATTERN.test(str) || SYMBOL_PATTERN.test(str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +77,7 @@ function mightContainEmoji(str: string) {
 | 
			
		|||
 * @param {String} char The emoji character
 | 
			
		||||
 * @return {String} The shortcode (such as :thumbup:)
 | 
			
		||||
 */
 | 
			
		||||
export function unicodeToShortcode(char: string) {
 | 
			
		||||
export function unicodeToShortcode(char: string): string {
 | 
			
		||||
    const data = getEmojiFromUnicode(char);
 | 
			
		||||
    return (data && data.shortcodes ? `:${data.shortcodes[0]}:` : '');
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +88,7 @@ export function unicodeToShortcode(char: string) {
 | 
			
		|||
 * @param {String} shortcode The shortcode (such as :thumbup:)
 | 
			
		||||
 * @return {String} The emoji character; null if none exists
 | 
			
		||||
 */
 | 
			
		||||
export function shortcodeToUnicode(shortcode: string) {
 | 
			
		||||
export function shortcodeToUnicode(shortcode: string): string {
 | 
			
		||||
    shortcode = shortcode.slice(1, shortcode.length - 1);
 | 
			
		||||
    const data = SHORTCODE_TO_EMOJI.get(shortcode);
 | 
			
		||||
    return data ? data.unicode : null;
 | 
			
		||||
| 
						 | 
				
			
			@ -124,13 +125,13 @@ export function processHtmlForSending(html: string): string {
 | 
			
		|||
 * Given an untrusted HTML string, return a React node with an sanitized version
 | 
			
		||||
 * of that HTML.
 | 
			
		||||
 */
 | 
			
		||||
export function sanitizedHtmlNode(insaneHtml: string) {
 | 
			
		||||
export function sanitizedHtmlNode(insaneHtml: string): ReactNode {
 | 
			
		||||
    const saneHtml = sanitizeHtml(insaneHtml, sanitizeHtmlParams);
 | 
			
		||||
 | 
			
		||||
    return <div dangerouslySetInnerHTML={{ __html: saneHtml }} dir="auto" />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getHtmlText(insaneHtml: string) {
 | 
			
		||||
export function getHtmlText(insaneHtml: string): string {
 | 
			
		||||
    return sanitizeHtml(insaneHtml, {
 | 
			
		||||
        allowedTags: [],
 | 
			
		||||
        allowedAttributes: {},
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +149,7 @@ export function getHtmlText(insaneHtml: string) {
 | 
			
		|||
 * other places we need to sanitise URLs.
 | 
			
		||||
 * @return true if permitted, otherwise false
 | 
			
		||||
 */
 | 
			
		||||
export function isUrlPermitted(inputUrl: string) {
 | 
			
		||||
export function isUrlPermitted(inputUrl: string): boolean {
 | 
			
		||||
    try {
 | 
			
		||||
        const parsed = url.parse(inputUrl);
 | 
			
		||||
        if (!parsed.protocol) return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -351,13 +352,6 @@ class HtmlHighlighter extends BaseHighlighter<string> {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IContent {
 | 
			
		||||
    format?: string;
 | 
			
		||||
    // eslint-disable-next-line camelcase
 | 
			
		||||
    formatted_body?: string;
 | 
			
		||||
    body: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IOpts {
 | 
			
		||||
    highlightLink?: string;
 | 
			
		||||
    disableBigEmoji?: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -367,6 +361,14 @@ interface IOpts {
 | 
			
		|||
    ref?: React.Ref<any>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IOptsReturnNode extends IOpts {
 | 
			
		||||
    returnString: false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IOptsReturnString extends IOpts {
 | 
			
		||||
    returnString: true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* turn a matrix event body into html
 | 
			
		||||
 *
 | 
			
		||||
 * content: 'content' of the MatrixEvent
 | 
			
		||||
| 
						 | 
				
			
			@ -380,6 +382,8 @@ interface IOpts {
 | 
			
		|||
 * opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer
 | 
			
		||||
 * opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString)
 | 
			
		||||
 */
 | 
			
		||||
export function bodyToHtml(content: IContent, highlights: string[], opts: IOptsReturnString): string;
 | 
			
		||||
export function bodyToHtml(content: IContent, highlights: string[], opts: IOptsReturnNode): ReactNode;
 | 
			
		||||
export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts = {}) {
 | 
			
		||||
    const isHtmlMessage = content.format === "org.matrix.custom.html" && content.formatted_body;
 | 
			
		||||
    let bodyHasEmoji = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -501,7 +505,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
 | 
			
		|||
 * @param {object} [options] Options for linkifyString. Default: linkifyMatrix.options
 | 
			
		||||
 * @returns {string} Linkified string
 | 
			
		||||
 */
 | 
			
		||||
export function linkifyString(str: string, options = linkifyMatrix.options) {
 | 
			
		||||
export function linkifyString(str: string, options = linkifyMatrix.options): string {
 | 
			
		||||
    return _linkifyString(str, options);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -512,7 +516,7 @@ export function linkifyString(str: string, options = linkifyMatrix.options) {
 | 
			
		|||
 * @param {object} [options] Options for linkifyElement. Default: linkifyMatrix.options
 | 
			
		||||
 * @returns {object}
 | 
			
		||||
 */
 | 
			
		||||
export function linkifyElement(element: HTMLElement, options = linkifyMatrix.options) {
 | 
			
		||||
export function linkifyElement(element: HTMLElement, options = linkifyMatrix.options): HTMLElement {
 | 
			
		||||
    return _linkifyElement(element, options);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -523,7 +527,7 @@ export function linkifyElement(element: HTMLElement, options = linkifyMatrix.opt
 | 
			
		|||
 * @param {object} [options] Options for linkifyString. Default: linkifyMatrix.options
 | 
			
		||||
 * @returns {string}
 | 
			
		||||
 */
 | 
			
		||||
export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatrix.options) {
 | 
			
		||||
export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatrix.options): string {
 | 
			
		||||
    return sanitizeHtml(linkifyString(dirtyHtml, options), sanitizeHtmlParams);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -534,7 +538,7 @@ export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatri
 | 
			
		|||
 * @param {Node} node
 | 
			
		||||
 * @returns {bool}
 | 
			
		||||
 */
 | 
			
		||||
export function checkBlockNode(node: Node) {
 | 
			
		||||
export function checkBlockNode(node: Node): boolean {
 | 
			
		||||
    switch (node.nodeName) {
 | 
			
		||||
        case "H1":
 | 
			
		||||
        case "H2":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2015, 2016 OpenMarket Ltd
 | 
			
		||||
Copyright 2015 - 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,7 +14,9 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import {MatrixClientPeg} from './MatrixClientPeg';
 | 
			
		||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
			
		||||
 | 
			
		||||
import { MatrixClientPeg } from './MatrixClientPeg';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given a room object, return the alias we should use for it,
 | 
			
		||||
| 
						 | 
				
			
			@ -25,11 +27,11 @@ import {MatrixClientPeg} from './MatrixClientPeg';
 | 
			
		|||
 * @param {Object} room The room object
 | 
			
		||||
 * @returns {string} A display alias for the given room
 | 
			
		||||
 */
 | 
			
		||||
export function getDisplayAliasForRoom(room) {
 | 
			
		||||
export function getDisplayAliasForRoom(room: Room): string {
 | 
			
		||||
    return room.getCanonicalAlias() || room.getAltAliases()[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function looksLikeDirectMessageRoom(room, myUserId) {
 | 
			
		||||
export function looksLikeDirectMessageRoom(room: Room, myUserId: string): boolean {
 | 
			
		||||
    const myMembership = room.getMyMembership();
 | 
			
		||||
    const me = room.getMember(myUserId);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +50,7 @@ export function looksLikeDirectMessageRoom(room, myUserId) {
 | 
			
		|||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function guessAndSetDMRoom(room, isDirect) {
 | 
			
		||||
export function guessAndSetDMRoom(room: Room, isDirect: boolean): Promise<void> {
 | 
			
		||||
    let newTarget;
 | 
			
		||||
    if (isDirect) {
 | 
			
		||||
        const guessedUserId = guessDMRoomTargetId(
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +72,7 @@ export function guessAndSetDMRoom(room, isDirect) {
 | 
			
		|||
                   this room as a DM room
 | 
			
		||||
 * @returns {object} A promise
 | 
			
		||||
 */
 | 
			
		||||
export function setDMRoom(roomId, userId) {
 | 
			
		||||
export function setDMRoom(roomId: string, userId: string): Promise<void> {
 | 
			
		||||
    if (MatrixClientPeg.get().isGuest()) {
 | 
			
		||||
        return Promise.resolve();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +116,7 @@ export function setDMRoom(roomId, userId) {
 | 
			
		|||
 * @param {string} myUserId User ID of the current user
 | 
			
		||||
 * @returns {string} User ID of the user that the room is probably a DM with
 | 
			
		||||
 */
 | 
			
		||||
function guessDMRoomTargetId(room, myUserId) {
 | 
			
		||||
function guessDMRoomTargetId(room: Room, myUserId: string): string {
 | 
			
		||||
    let oldestTs;
 | 
			
		||||
    let oldestUser;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2015, 2016 OpenMarket Ltd
 | 
			
		||||
Copyright 2015 - 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,9 +14,13 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import {MatrixClientPeg} from "./MatrixClientPeg";
 | 
			
		||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
			
		||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
import { EventType, MsgType } from "matrix-js-sdk/src/@types/event";
 | 
			
		||||
 | 
			
		||||
import { MatrixClientPeg } from "./MatrixClientPeg";
 | 
			
		||||
import shouldHideEvent from './shouldHideEvent';
 | 
			
		||||
import {haveTileForEvent} from "./components/views/rooms/EventTile";
 | 
			
		||||
import { haveTileForEvent } from "./components/views/rooms/EventTile";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns true iff this event arriving in a room should affect the room's
 | 
			
		||||
| 
						 | 
				
			
			@ -25,28 +29,33 @@ import {haveTileForEvent} from "./components/views/rooms/EventTile";
 | 
			
		|||
 * @param {Object} ev The event
 | 
			
		||||
 * @returns {boolean} True if the given event should affect the unread message count
 | 
			
		||||
 */
 | 
			
		||||
export function eventTriggersUnreadCount(ev) {
 | 
			
		||||
export function eventTriggersUnreadCount(ev: MatrixEvent): boolean {
 | 
			
		||||
    if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) {
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (ev.getType() == 'm.room.member') {
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (ev.getType() == 'm.room.third_party_invite') {
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (ev.getType() == 'm.room.aliases' || ev.getType() == 'm.room.canonical_alias') {
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (ev.getType() == 'm.room.server_acl') {
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (ev.isRedacted()) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (ev.getType()) {
 | 
			
		||||
        case EventType.RoomMember:
 | 
			
		||||
        case EventType.RoomThirdPartyInvite:
 | 
			
		||||
        case EventType.CallAnswer:
 | 
			
		||||
        case EventType.CallHangup:
 | 
			
		||||
        case EventType.RoomAliases:
 | 
			
		||||
        case EventType.RoomCanonicalAlias:
 | 
			
		||||
        case EventType.RoomServerAcl:
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        case EventType.RoomMessage:
 | 
			
		||||
            if (ev.getContent().msgtype === MsgType.Notice) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ev.isRedacted()) return false;
 | 
			
		||||
    return haveTileForEvent(ev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function doesRoomHaveUnreadMessages(room) {
 | 
			
		||||
export function doesRoomHaveUnreadMessages(room: Room): boolean {
 | 
			
		||||
    const myUserId = MatrixClientPeg.get().getUserId();
 | 
			
		||||
 | 
			
		||||
    // get the most recent read receipt sent by our account.
 | 
			
		||||
| 
						 | 
				
			
			@ -1461,7 +1461,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		|||
        });
 | 
			
		||||
 | 
			
		||||
        const dft = new DecryptionFailureTracker((total, errorCode) => {
 | 
			
		||||
            Analytics.trackEvent('E2E', 'Decryption failure', errorCode, total);
 | 
			
		||||
            Analytics.trackEvent('E2E', 'Decryption failure', errorCode, String(total));
 | 
			
		||||
            CountlyAnalytics.instance.track("decryption_failure", { errorCode }, null, { sum: total });
 | 
			
		||||
        }, (errorCode) => {
 | 
			
		||||
            // Map JS-SDK error codes to tracker codes for aggregation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,30 +14,31 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import React, {useMemo, useState, useEffect} from "react";
 | 
			
		||||
import React, { useMemo, useState, useEffect } from "react";
 | 
			
		||||
import classnames from "classnames";
 | 
			
		||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
 | 
			
		||||
import {Room} from "matrix-js-sdk/src/models/room";
 | 
			
		||||
import {MatrixClient} from "matrix-js-sdk/src/client";
 | 
			
		||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
			
		||||
import { MatrixClient } from "matrix-js-sdk/src/client";
 | 
			
		||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
 | 
			
		||||
 | 
			
		||||
import {_t} from "../../../languageHandler";
 | 
			
		||||
import { _t } from "../../../languageHandler";
 | 
			
		||||
import dis from "../../../dispatcher/dispatcher";
 | 
			
		||||
import {useSettingValue, useFeatureEnabled} from "../../../hooks/useSettings";
 | 
			
		||||
import {UIFeature} from "../../../settings/UIFeature";
 | 
			
		||||
import {Layout} from "../../../settings/Layout";
 | 
			
		||||
import {IDialogProps} from "./IDialogProps";
 | 
			
		||||
import { useSettingValue, useFeatureEnabled } from "../../../hooks/useSettings";
 | 
			
		||||
import { UIFeature } from "../../../settings/UIFeature";
 | 
			
		||||
import { Layout } from "../../../settings/Layout";
 | 
			
		||||
import { IDialogProps } from "./IDialogProps";
 | 
			
		||||
import BaseDialog from "./BaseDialog";
 | 
			
		||||
import {avatarUrlForUser} from "../../../Avatar";
 | 
			
		||||
import { avatarUrlForUser } from "../../../Avatar";
 | 
			
		||||
import EventTile from "../rooms/EventTile";
 | 
			
		||||
import SearchBox from "../../structures/SearchBox";
 | 
			
		||||
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
 | 
			
		||||
import {Alignment} from '../elements/Tooltip';
 | 
			
		||||
import { Alignment } from '../elements/Tooltip';
 | 
			
		||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
 | 
			
		||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
 | 
			
		||||
import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState";
 | 
			
		||||
import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
 | 
			
		||||
import NotificationBadge from "../rooms/NotificationBadge";
 | 
			
		||||
import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
 | 
			
		||||
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
 | 
			
		||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
 | 
			
		||||
import { sortRooms } from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
 | 
			
		||||
import QueryMatcher from "../../../autocomplete/QueryMatcher";
 | 
			
		||||
 | 
			
		||||
const AVATAR_SIZE = 30;
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +172,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
 | 
			
		|||
            );
 | 
			
		||||
        },
 | 
			
		||||
        getMxcAvatarUrl: () => profileInfo.avatar_url,
 | 
			
		||||
    };
 | 
			
		||||
    } as RoomMember;
 | 
			
		||||
 | 
			
		||||
    const [query, setQuery] = useState("");
 | 
			
		||||
    const lcQuery = query.toLowerCase();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,13 +17,14 @@ limitations under the License.
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
 | 
			
		||||
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
 | 
			
		||||
 | 
			
		||||
import * as Avatar from '../../../Avatar';
 | 
			
		||||
import EventTile from '../rooms/EventTile';
 | 
			
		||||
import SettingsStore from "../../../settings/SettingsStore";
 | 
			
		||||
import {Layout} from "../../../settings/Layout";
 | 
			
		||||
import {UIFeature} from "../../../settings/UIFeature";
 | 
			
		||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
 | 
			
		||||
import { Layout } from "../../../settings/Layout";
 | 
			
		||||
import { UIFeature } from "../../../settings/UIFeature";
 | 
			
		||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
 | 
			
		||||
 | 
			
		||||
interface IProps {
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -111,7 +112,7 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
 | 
			
		|||
                );
 | 
			
		||||
            },
 | 
			
		||||
            getMxcAvatarUrl: () => this.props.avatarUrl,
 | 
			
		||||
        };
 | 
			
		||||
        } as RoomMember;
 | 
			
		||||
 | 
			
		||||
        return event;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import RateLimitedFunc from '../../../ratelimitedfunc';
 | 
			
		|||
import SettingsStore from "../../../settings/SettingsStore";
 | 
			
		||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
 | 
			
		||||
import { UIFeature } from "../../../settings/UIFeature";
 | 
			
		||||
import { ResizeNotifier } from "../../../utils/ResizeNotifier";
 | 
			
		||||
import ResizeNotifier from "../../../utils/ResizeNotifier";
 | 
			
		||||
import CallViewForRoom from '../voip/CallViewForRoom';
 | 
			
		||||
import { objectHasDiff } from "../../../utils/objects";
 | 
			
		||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,6 @@ limitations under the License.
 | 
			
		|||
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import classNames from "classnames";
 | 
			
		||||
 | 
			
		||||
import { EventType } from "matrix-js-sdk/src/@types/event";
 | 
			
		||||
import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
import { Relations } from "matrix-js-sdk/src/models/relations";
 | 
			
		||||
| 
						 | 
				
			
			@ -29,24 +28,24 @@ import { hasText } from "../../../TextForEvent";
 | 
			
		|||
import * as sdk from "../../../index";
 | 
			
		||||
import dis from '../../../dispatcher/dispatcher';
 | 
			
		||||
import SettingsStore from "../../../settings/SettingsStore";
 | 
			
		||||
import {Layout} from "../../../settings/Layout";
 | 
			
		||||
import {formatTime} from "../../../DateUtils";
 | 
			
		||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
 | 
			
		||||
import {ALL_RULE_TYPES} from "../../../mjolnir/BanList";
 | 
			
		||||
import { Layout } from "../../../settings/Layout";
 | 
			
		||||
import { formatTime } from "../../../DateUtils";
 | 
			
		||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
 | 
			
		||||
import { ALL_RULE_TYPES } from "../../../mjolnir/BanList";
 | 
			
		||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
			
		||||
import {E2E_STATE} from "./E2EIcon";
 | 
			
		||||
import {toRem} from "../../../utils/units";
 | 
			
		||||
import {WidgetType} from "../../../widgets/WidgetType";
 | 
			
		||||
import { E2E_STATE } from "./E2EIcon";
 | 
			
		||||
import { toRem } from "../../../utils/units";
 | 
			
		||||
import { WidgetType } from "../../../widgets/WidgetType";
 | 
			
		||||
import RoomAvatar from "../avatars/RoomAvatar";
 | 
			
		||||
import {WIDGET_LAYOUT_EVENT_TYPE} from "../../../stores/widgets/WidgetLayoutStore";
 | 
			
		||||
import {objectHasDiff} from "../../../utils/objects";
 | 
			
		||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
 | 
			
		||||
import { WIDGET_LAYOUT_EVENT_TYPE } from "../../../stores/widgets/WidgetLayoutStore";
 | 
			
		||||
import { objectHasDiff } from "../../../utils/objects";
 | 
			
		||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
 | 
			
		||||
import Tooltip from "../elements/Tooltip";
 | 
			
		||||
import { EditorStateTransfer } from "../../../utils/EditorStateTransfer";
 | 
			
		||||
import EditorStateTransfer from "../../../utils/EditorStateTransfer";
 | 
			
		||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
 | 
			
		||||
import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState";
 | 
			
		||||
import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
 | 
			
		||||
import NotificationBadge from "./NotificationBadge";
 | 
			
		||||
import {ComposerInsertPayload} from "../../../dispatcher/payloads/ComposerInsertPayload";
 | 
			
		||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
 | 
			
		||||
import { Action } from '../../../dispatcher/actions';
 | 
			
		||||
 | 
			
		||||
const eventTileTypes = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ import { EventType } from "matrix-js-sdk/src/@types/event";
 | 
			
		|||
 | 
			
		||||
import { _t, _td } from "../../../languageHandler";
 | 
			
		||||
import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
 | 
			
		||||
import { ResizeNotifier } from "../../../utils/ResizeNotifier";
 | 
			
		||||
import ResizeNotifier from "../../../utils/ResizeNotifier";
 | 
			
		||||
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore";
 | 
			
		||||
import RoomViewStore from "../../../stores/RoomViewStore";
 | 
			
		||||
import { ITagMap } from "../../../stores/room-list/algorithms/models";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ import { ActionPayload } from "../../../dispatcher/payloads";
 | 
			
		|||
import { Enable, Resizable } from "re-resizable";
 | 
			
		||||
import { Direction } from "re-resizable/lib/resizer";
 | 
			
		||||
import { polyfillTouchEvent } from "../../../@types/polyfill";
 | 
			
		||||
import { ResizeNotifier } from "../../../utils/ResizeNotifier";
 | 
			
		||||
import ResizeNotifier from "../../../utils/ResizeNotifier";
 | 
			
		||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
 | 
			
		||||
import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore";
 | 
			
		||||
import { arrayFastClone, arrayHasOrderChange } from "../../../utils/arrays";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -112,7 +112,7 @@ export interface IVariables {
 | 
			
		|||
    [key: string]: SubstitutionValue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Tags = Record<string, SubstitutionValue>;
 | 
			
		||||
export type Tags = Record<string, SubstitutionValue>;
 | 
			
		||||
 | 
			
		||||
export type TranslatedString = string | React.ReactNode;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2019 The Matrix.org Foundation C.I.C.
 | 
			
		||||
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,36 +14,40 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
 | 
			
		||||
import { SerializedPart } from "../editor/parts";
 | 
			
		||||
import { Caret } from "../editor/caret";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Used while editing, to pass the event, and to preserve editor state
 | 
			
		||||
 * from one editor instance to another when remounting the editor
 | 
			
		||||
 * upon receiving the remote echo for an unsent event.
 | 
			
		||||
 */
 | 
			
		||||
export default class EditorStateTransfer {
 | 
			
		||||
    constructor(event) {
 | 
			
		||||
        this._event = event;
 | 
			
		||||
        this._serializedParts = null;
 | 
			
		||||
        this.caret = null;
 | 
			
		||||
    private serializedParts: SerializedPart[] = null;
 | 
			
		||||
    private caret: Caret = null;
 | 
			
		||||
 | 
			
		||||
    constructor(private readonly event: MatrixEvent) {}
 | 
			
		||||
 | 
			
		||||
    public setEditorState(caret: Caret, serializedParts: SerializedPart[]) {
 | 
			
		||||
        this.caret = caret;
 | 
			
		||||
        this.serializedParts = serializedParts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setEditorState(caret, serializedParts) {
 | 
			
		||||
        this._caret = caret;
 | 
			
		||||
        this._serializedParts = serializedParts;
 | 
			
		||||
    public hasEditorState() {
 | 
			
		||||
        return !!this.serializedParts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hasEditorState() {
 | 
			
		||||
        return !!this._serializedParts;
 | 
			
		||||
    public getSerializedParts() {
 | 
			
		||||
        return this.serializedParts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getSerializedParts() {
 | 
			
		||||
        return this._serializedParts;
 | 
			
		||||
    public getCaret() {
 | 
			
		||||
        return this.caret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getCaret() {
 | 
			
		||||
        return this._caret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getEvent() {
 | 
			
		||||
        return this._event;
 | 
			
		||||
    public getEvent() {
 | 
			
		||||
        return this.event;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2018 New Vector Ltd
 | 
			
		||||
Copyright 2018 - 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,7 +14,10 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { _t, _td } from '../languageHandler';
 | 
			
		||||
import React, { ReactNode } from "react";
 | 
			
		||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
 | 
			
		||||
 | 
			
		||||
import { _t, _td, Tags, TranslatedString } from '../languageHandler';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Produce a translated error message for a
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +33,12 @@ import { _t, _td } from '../languageHandler';
 | 
			
		|||
 *     for any tags in the strings apart from 'a'
 | 
			
		||||
 * @returns {*} Translated string or react component
 | 
			
		||||
 */
 | 
			
		||||
export function messageForResourceLimitError(limitType, adminContact, strings, extraTranslations) {
 | 
			
		||||
export function messageForResourceLimitError(
 | 
			
		||||
    limitType: string,
 | 
			
		||||
    adminContact: string,
 | 
			
		||||
    strings: Record<string, string>,
 | 
			
		||||
    extraTranslations?: Tags,
 | 
			
		||||
): TranslatedString {
 | 
			
		||||
    let errString = strings[limitType];
 | 
			
		||||
    if (errString === undefined) errString = strings[''];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +57,7 @@ export function messageForResourceLimitError(limitType, adminContact, strings, e
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function messageForSyncError(err) {
 | 
			
		||||
export function messageForSyncError(err: MatrixError | Error): ReactNode {
 | 
			
		||||
    if (err.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') {
 | 
			
		||||
        const limitError = messageForResourceLimitError(
 | 
			
		||||
            err.data.limit_type,
 | 
			
		||||
| 
						 | 
				
			
			@ -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,9 +14,12 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { EventStatus } from 'matrix-js-sdk/src/models/event';
 | 
			
		||||
import {MatrixClientPeg} from '../MatrixClientPeg';
 | 
			
		||||
import { Room } from 'matrix-js-sdk/src/models/room';
 | 
			
		||||
import { MatrixEvent, EventStatus } from 'matrix-js-sdk/src/models/event';
 | 
			
		||||
 | 
			
		||||
import { MatrixClientPeg } from '../MatrixClientPeg';
 | 
			
		||||
import shouldHideEvent from "../shouldHideEvent";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns whether an event should allow actions like reply, reactions, edit, etc.
 | 
			
		||||
 * which effectively checks whether it's a regular message that has been sent and that we
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +28,7 @@ import shouldHideEvent from "../shouldHideEvent";
 | 
			
		|||
 * @param {MatrixEvent} mxEvent The event to check
 | 
			
		||||
 * @returns {boolean} true if actionable
 | 
			
		||||
 */
 | 
			
		||||
export function isContentActionable(mxEvent) {
 | 
			
		||||
export function isContentActionable(mxEvent: MatrixEvent): boolean {
 | 
			
		||||
    const { status: eventStatus } = mxEvent;
 | 
			
		||||
 | 
			
		||||
    // status is SENT before remote-echo, null after
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +48,7 @@ export function isContentActionable(mxEvent) {
 | 
			
		|||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function canEditContent(mxEvent) {
 | 
			
		||||
export function canEditContent(mxEvent: MatrixEvent): boolean {
 | 
			
		||||
    if (mxEvent.status === EventStatus.CANCELLED || mxEvent.getType() !== "m.room.message" || mxEvent.isRedacted()) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +59,7 @@ export function canEditContent(mxEvent) {
 | 
			
		|||
        mxEvent.getSender() === MatrixClientPeg.get().getUserId();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function canEditOwnEvent(mxEvent) {
 | 
			
		||||
export function canEditOwnEvent(mxEvent: MatrixEvent): boolean {
 | 
			
		||||
    // for now we only allow editing
 | 
			
		||||
    // your own events. So this just call through
 | 
			
		||||
    // In the future though, moderators will be able to
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +70,7 @@ export function canEditOwnEvent(mxEvent) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
const MAX_JUMP_DISTANCE = 100;
 | 
			
		||||
export function findEditableEvent(room, isForward, fromEventId = undefined) {
 | 
			
		||||
export function findEditableEvent(room: Room, isForward: boolean, fromEventId: string = undefined): MatrixEvent {
 | 
			
		||||
    const liveTimeline = room.getLiveTimeline();
 | 
			
		||||
    const events = liveTimeline.getEvents().concat(room.getPendingEvents());
 | 
			
		||||
    const maxIdx = events.length - 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2019 The Matrix.org Foundation C.I.C.
 | 
			
		||||
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.
 | 
			
		||||
| 
						 | 
				
			
			@ -15,14 +15,15 @@ limitations under the License.
 | 
			
		|||
*/
 | 
			
		||||
 | 
			
		||||
import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types';
 | 
			
		||||
 | 
			
		||||
import SdkConfig from '../SdkConfig';
 | 
			
		||||
import {MatrixClientPeg} from '../MatrixClientPeg';
 | 
			
		||||
 | 
			
		||||
export function getDefaultIdentityServerUrl() {
 | 
			
		||||
export function getDefaultIdentityServerUrl(): string {
 | 
			
		||||
    return SdkConfig.get()['validated_server_config']['isUrl'];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useDefaultIdentityServer() {
 | 
			
		||||
export function useDefaultIdentityServer(): void {
 | 
			
		||||
    const url = getDefaultIdentityServerUrl();
 | 
			
		||||
    // Account data change will update localstorage, client, etc through dispatcher
 | 
			
		||||
    MatrixClientPeg.get().setAccountData("m.identity_server", {
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +31,7 @@ export function useDefaultIdentityServer() {
 | 
			
		|||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function doesIdentityServerHaveTerms(fullUrl) {
 | 
			
		||||
export async function doesIdentityServerHaveTerms(fullUrl: string): Promise<boolean> {
 | 
			
		||||
    let terms;
 | 
			
		||||
    try {
 | 
			
		||||
        terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IS, fullUrl);
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +47,7 @@ export async function doesIdentityServerHaveTerms(fullUrl) {
 | 
			
		|||
    return terms && terms["policies"] && (Object.keys(terms["policies"]).length > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function doesAccountDataHaveIdentityServer() {
 | 
			
		||||
export function doesAccountDataHaveIdentityServer(): boolean {
 | 
			
		||||
    const event = MatrixClientPeg.get().getAccountData("m.identity_server");
 | 
			
		||||
    return event && event.getContent() && event.getContent()['base_url'];
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2019 The Matrix.org Foundation C.I.C.
 | 
			
		||||
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,31 +14,33 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { ReactNode } from 'react';
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
import DiffMatchPatch from 'diff-match-patch';
 | 
			
		||||
import {DiffDOM} from "diff-dom";
 | 
			
		||||
import { checkBlockNode, bodyToHtml } from "../HtmlUtils";
 | 
			
		||||
import { diff_match_patch as DiffMatchPatch } from 'diff-match-patch';
 | 
			
		||||
import { Action, DiffDOM, IDiff } from "diff-dom";
 | 
			
		||||
import { IContent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
 | 
			
		||||
import { bodyToHtml, checkBlockNode, IOptsReturnString } from "../HtmlUtils";
 | 
			
		||||
 | 
			
		||||
const decodeEntities = (function() {
 | 
			
		||||
    let textarea = null;
 | 
			
		||||
    return function(string) {
 | 
			
		||||
    return function(str: string): string {
 | 
			
		||||
        if (!textarea) {
 | 
			
		||||
            textarea = document.createElement("textarea");
 | 
			
		||||
        }
 | 
			
		||||
        textarea.innerHTML = string;
 | 
			
		||||
        textarea.innerHTML = str;
 | 
			
		||||
        return textarea.value;
 | 
			
		||||
    };
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
function textToHtml(text) {
 | 
			
		||||
function textToHtml(text: string): string {
 | 
			
		||||
    const container = document.createElement("div");
 | 
			
		||||
    container.textContent = text;
 | 
			
		||||
    return container.innerHTML;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getSanitizedHtmlBody(content) {
 | 
			
		||||
    const opts = {
 | 
			
		||||
function getSanitizedHtmlBody(content: IContent): string {
 | 
			
		||||
    const opts: IOptsReturnString = {
 | 
			
		||||
        stripReplyFallback: true,
 | 
			
		||||
        returnString: true,
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			@ -57,21 +59,21 @@ function getSanitizedHtmlBody(content) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function wrapInsertion(child) {
 | 
			
		||||
function wrapInsertion(child: Node): HTMLElement {
 | 
			
		||||
    const wrapper = document.createElement(checkBlockNode(child) ? "div" : "span");
 | 
			
		||||
    wrapper.className = "mx_EditHistoryMessage_insertion";
 | 
			
		||||
    wrapper.appendChild(child);
 | 
			
		||||
    return wrapper;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function wrapDeletion(child) {
 | 
			
		||||
function wrapDeletion(child: Node): HTMLElement {
 | 
			
		||||
    const wrapper = document.createElement(checkBlockNode(child) ? "div" : "span");
 | 
			
		||||
    wrapper.className = "mx_EditHistoryMessage_deletion";
 | 
			
		||||
    wrapper.appendChild(child);
 | 
			
		||||
    return wrapper;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findRefNodes(root, route, isAddition) {
 | 
			
		||||
function findRefNodes(root: Node, route: number[], isAddition = false) {
 | 
			
		||||
    let refNode = root;
 | 
			
		||||
    let refParentNode;
 | 
			
		||||
    const end = isAddition ? route.length - 1 : route.length;
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +81,7 @@ function findRefNodes(root, route, isAddition) {
 | 
			
		|||
        refParentNode = refNode;
 | 
			
		||||
        refNode = refNode.childNodes[route[i]];
 | 
			
		||||
    }
 | 
			
		||||
    return {refNode, refParentNode};
 | 
			
		||||
    return { refNode, refParentNode };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function diffTreeToDOM(desc) {
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +103,7 @@ function diffTreeToDOM(desc) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function insertBefore(parent, nextSibling, child) {
 | 
			
		||||
function insertBefore(parent: Node, nextSibling: Node | null, child: Node): void {
 | 
			
		||||
    if (nextSibling) {
 | 
			
		||||
        parent.insertBefore(child, nextSibling);
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +111,7 @@ function insertBefore(parent, nextSibling, child) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isRouteOfNextSibling(route1, route2) {
 | 
			
		||||
function isRouteOfNextSibling(route1: number[], route2: number[]): boolean {
 | 
			
		||||
    // routes are arrays with indices,
 | 
			
		||||
    // to be interpreted as a path in the dom tree
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -127,7 +129,7 @@ function isRouteOfNextSibling(route1, route2) {
 | 
			
		|||
    return route2[lastD1Idx] >= route1[lastD1Idx];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function adjustRoutes(diff, remainingDiffs) {
 | 
			
		||||
function adjustRoutes(diff: IDiff, remainingDiffs: IDiff[]): void {
 | 
			
		||||
    if (diff.action === "removeTextElement" || diff.action === "removeElement") {
 | 
			
		||||
        // as removed text is not removed from the html, but marked as deleted,
 | 
			
		||||
        // we need to readjust indices that assume the current node has been removed.
 | 
			
		||||
| 
						 | 
				
			
			@ -140,14 +142,14 @@ function adjustRoutes(diff, remainingDiffs) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function stringAsTextNode(string) {
 | 
			
		||||
function stringAsTextNode(string: string): Text {
 | 
			
		||||
    return document.createTextNode(decodeEntities(string));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) {
 | 
			
		||||
function renderDifferenceInDOM(originalRootNode: Node, diff: IDiff, diffMathPatch: DiffMatchPatch): void {
 | 
			
		||||
    const {refNode, refParentNode} = findRefNodes(originalRootNode, diff.route);
 | 
			
		||||
    switch (diff.action) {
 | 
			
		||||
        case "replaceElement": {
 | 
			
		||||
        case Action.ReplaceElement: {
 | 
			
		||||
            const container = document.createElement("span");
 | 
			
		||||
            const delNode = wrapDeletion(diffTreeToDOM(diff.oldValue));
 | 
			
		||||
            const insNode = wrapInsertion(diffTreeToDOM(diff.newValue));
 | 
			
		||||
| 
						 | 
				
			
			@ -156,22 +158,22 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) {
 | 
			
		|||
            refNode.parentNode.replaceChild(container, refNode);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case "removeTextElement": {
 | 
			
		||||
        case Action.RemoveTextElement: {
 | 
			
		||||
            const delNode = wrapDeletion(stringAsTextNode(diff.value));
 | 
			
		||||
            refNode.parentNode.replaceChild(delNode, refNode);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case "removeElement": {
 | 
			
		||||
        case Action.RemoveElement: {
 | 
			
		||||
            const delNode = wrapDeletion(diffTreeToDOM(diff.element));
 | 
			
		||||
            refNode.parentNode.replaceChild(delNode, refNode);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case "modifyTextElement": {
 | 
			
		||||
        case Action.ModifyTextElement: {
 | 
			
		||||
            const textDiffs = diffMathPatch.diff_main(diff.oldValue, diff.newValue);
 | 
			
		||||
            diffMathPatch.diff_cleanupSemantic(textDiffs);
 | 
			
		||||
            const container = document.createElement("span");
 | 
			
		||||
            for (const [modifier, text] of textDiffs) {
 | 
			
		||||
                let textDiffNode = stringAsTextNode(text);
 | 
			
		||||
                let textDiffNode: Node = stringAsTextNode(text);
 | 
			
		||||
                if (modifier < 0) {
 | 
			
		||||
                    textDiffNode = wrapDeletion(textDiffNode);
 | 
			
		||||
                } else if (modifier > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -182,12 +184,12 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) {
 | 
			
		|||
            refNode.parentNode.replaceChild(container, refNode);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case "addElement": {
 | 
			
		||||
        case Action.AddElement: {
 | 
			
		||||
            const insNode = wrapInsertion(diffTreeToDOM(diff.element));
 | 
			
		||||
            insertBefore(refParentNode, refNode, insNode);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case "addTextElement": {
 | 
			
		||||
        case Action.AddTextElement: {
 | 
			
		||||
            // XXX: sometimes diffDOM says insert a newline when there shouldn't be one
 | 
			
		||||
            // but we must insert the node anyway so that we don't break the route child IDs.
 | 
			
		||||
            // See https://github.com/fiduswriter/diffDOM/issues/100
 | 
			
		||||
| 
						 | 
				
			
			@ -197,11 +199,11 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) {
 | 
			
		|||
        }
 | 
			
		||||
        // e.g. when changing a the href of a link,
 | 
			
		||||
        // show the link with old href as removed and with the new href as added
 | 
			
		||||
        case "removeAttribute":
 | 
			
		||||
        case "addAttribute":
 | 
			
		||||
        case "modifyAttribute": {
 | 
			
		||||
        case Action.RemoveAttribute:
 | 
			
		||||
        case Action.AddAttribute:
 | 
			
		||||
        case Action.ModifyAttribute: {
 | 
			
		||||
            const delNode = wrapDeletion(refNode.cloneNode(true));
 | 
			
		||||
            const updatedNode = refNode.cloneNode(true);
 | 
			
		||||
            const updatedNode = refNode.cloneNode(true) as HTMLElement;
 | 
			
		||||
            if (diff.action === "addAttribute" || diff.action === "modifyAttribute") {
 | 
			
		||||
                updatedNode.setAttribute(diff.name, diff.newValue);
 | 
			
		||||
            } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -220,12 +222,12 @@ function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function routeIsEqual(r1, r2) {
 | 
			
		||||
function routeIsEqual(r1: number[], r2: number[]): boolean {
 | 
			
		||||
    return r1.length === r2.length && !r1.some((e, i) => e !== r2[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// workaround for https://github.com/fiduswriter/diffDOM/issues/90
 | 
			
		||||
function filterCancelingOutDiffs(originalDiffActions) {
 | 
			
		||||
function filterCancelingOutDiffs(originalDiffActions: IDiff[]): IDiff[] {
 | 
			
		||||
    const diffActions = originalDiffActions.slice();
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < diffActions.length; ++i) {
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +254,7 @@ function filterCancelingOutDiffs(originalDiffActions) {
 | 
			
		|||
 * @param {object} editContent the content for the edit message
 | 
			
		||||
 * @return {object} a react element similar to what `bodyToHtml` returns
 | 
			
		||||
 */
 | 
			
		||||
export function editBodyDiffToHtml(originalContent, editContent) {
 | 
			
		||||
export function editBodyDiffToHtml(originalContent: IContent, editContent: IContent): ReactNode {
 | 
			
		||||
    // wrap the body in a div, DiffDOM needs a root element
 | 
			
		||||
    const originalBody = `<div>${getSanitizedHtmlBody(originalContent)}</div>`;
 | 
			
		||||
    const editBody = `<div>${getSanitizedHtmlBody(editContent)}</div>`;
 | 
			
		||||
| 
						 | 
				
			
			@ -14,13 +14,15 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
 | 
			
		||||
export default class PinningUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines if the given event may be pinned.
 | 
			
		||||
     * @param {MatrixEvent} event The event to check.
 | 
			
		||||
     * @return {boolean} True if the event may be pinned, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    static isPinnable(event) {
 | 
			
		||||
    static isPinnable(event: MatrixEvent): boolean {
 | 
			
		||||
        if (!event) return false;
 | 
			
		||||
        if (event.getType() !== "m.room.message") return false;
 | 
			
		||||
        if (event.isRedacted()) return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2016 OpenMarket Ltd
 | 
			
		||||
Copyright 2016 - 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,6 +14,8 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given MatrixEvent containing receipts, return the first
 | 
			
		||||
 * read receipt from the given user ID, or null if no such
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +25,7 @@ limitations under the License.
 | 
			
		|||
 * @param {string} userId A user ID
 | 
			
		||||
 * @returns {Object} Read receipt
 | 
			
		||||
 */
 | 
			
		||||
export function findReadReceiptFromUserId(receiptEvent, userId) {
 | 
			
		||||
export function findReadReceiptFromUserId(receiptEvent: MatrixEvent, userId: string): object | null {
 | 
			
		||||
    const receiptKeys = Object.keys(receiptEvent.getContent());
 | 
			
		||||
    for (let i = 0; i < receiptKeys.length; ++i) {
 | 
			
		||||
        const rcpt = receiptEvent.getContent()[receiptKeys[i]];
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			@ -22,59 +22,58 @@ limitations under the License.
 | 
			
		|||
 * Fires when the middle panel has been resized by a pixel.
 | 
			
		||||
 * @event module:utils~ResizeNotifier#"middlePanelResizedNoisy"
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { EventEmitter } from "events";
 | 
			
		||||
import { throttle } from "lodash";
 | 
			
		||||
 | 
			
		||||
export default class ResizeNotifier extends EventEmitter {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
        // with default options, will call fn once at first call, and then every x ms
 | 
			
		||||
        // if there was another call in that timespan
 | 
			
		||||
        this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
 | 
			
		||||
        this._isResizing = false;
 | 
			
		||||
    }
 | 
			
		||||
    private _isResizing = false;
 | 
			
		||||
 | 
			
		||||
    get isResizing() {
 | 
			
		||||
    // with default options, will call fn once at first call, and then every x ms
 | 
			
		||||
    // if there was another call in that timespan
 | 
			
		||||
    private throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
 | 
			
		||||
 | 
			
		||||
    public get isResizing() {
 | 
			
		||||
        return this._isResizing;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    startResizing() {
 | 
			
		||||
    public startResizing() {
 | 
			
		||||
        this._isResizing = true;
 | 
			
		||||
        this.emit("isResizing", true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stopResizing() {
 | 
			
		||||
    public stopResizing() {
 | 
			
		||||
        this._isResizing = false;
 | 
			
		||||
        this.emit("isResizing", false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _noisyMiddlePanel() {
 | 
			
		||||
    private noisyMiddlePanel() {
 | 
			
		||||
        this.emit("middlePanelResizedNoisy");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateMiddlePanel() {
 | 
			
		||||
        this._throttledMiddlePanel();
 | 
			
		||||
        this._noisyMiddlePanel();
 | 
			
		||||
    private updateMiddlePanel() {
 | 
			
		||||
        this.throttledMiddlePanel();
 | 
			
		||||
        this.noisyMiddlePanel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // can be called in quick succession
 | 
			
		||||
    notifyLeftHandleResized() {
 | 
			
		||||
    public notifyLeftHandleResized() {
 | 
			
		||||
        // don't emit event for own region
 | 
			
		||||
        this._updateMiddlePanel();
 | 
			
		||||
        this.updateMiddlePanel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // can be called in quick succession
 | 
			
		||||
    notifyRightHandleResized() {
 | 
			
		||||
        this._updateMiddlePanel();
 | 
			
		||||
    public notifyRightHandleResized() {
 | 
			
		||||
        this.updateMiddlePanel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifyTimelineHeightChanged() {
 | 
			
		||||
        this._updateMiddlePanel();
 | 
			
		||||
    public notifyTimelineHeightChanged() {
 | 
			
		||||
        this.updateMiddlePanel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // can be called in quick succession
 | 
			
		||||
    notifyWindowResized() {
 | 
			
		||||
        this._updateMiddlePanel();
 | 
			
		||||
    public notifyWindowResized() {
 | 
			
		||||
        this.updateMiddlePanel();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2017 Vector Creations Ltd
 | 
			
		||||
Copyright 2017 - 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,10 +14,10 @@ See the License for the specific language governing permissions and
 | 
			
		|||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import {createClient} from "matrix-js-sdk/src/matrix";
 | 
			
		||||
import {IndexedDBCryptoStore} from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
 | 
			
		||||
import {WebStorageSessionStore} from "matrix-js-sdk/src/store/session/webstorage";
 | 
			
		||||
import {IndexedDBStore} from "matrix-js-sdk/src/store/indexeddb";
 | 
			
		||||
import { createClient, ICreateClientOpts } from "matrix-js-sdk/src/matrix";
 | 
			
		||||
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
 | 
			
		||||
import { WebStorageSessionStore } from "matrix-js-sdk/src/store/session/webstorage";
 | 
			
		||||
import { IndexedDBStore } from "matrix-js-sdk/src/store/indexeddb";
 | 
			
		||||
 | 
			
		||||
const localStorage = window.localStorage;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,8 +41,8 @@ try {
 | 
			
		|||
 *
 | 
			
		||||
 * @returns {MatrixClient} the newly-created MatrixClient
 | 
			
		||||
 */
 | 
			
		||||
export default function createMatrixClient(opts) {
 | 
			
		||||
    const storeOpts = {
 | 
			
		||||
export default function createMatrixClient(opts: ICreateClientOpts) {
 | 
			
		||||
    const storeOpts: Partial<ICreateClientOpts> = {
 | 
			
		||||
        useAuthorizationHeader: true,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,9 +65,10 @@ export default function createMatrixClient(opts) {
 | 
			
		|||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts = Object.assign(storeOpts, opts);
 | 
			
		||||
 | 
			
		||||
    return createClient(opts);
 | 
			
		||||
    return createClient({
 | 
			
		||||
        ...storeOpts,
 | 
			
		||||
        ...opts,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
createMatrixClient.indexedDbWorkerScript = null;
 | 
			
		||||
| 
						 | 
				
			
			@ -1486,6 +1486,11 @@
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/@types/counterpart/-/counterpart-0.18.1.tgz#b1b784d9e54d9879f0a8cb12f2caedab65430fe8"
 | 
			
		||||
  integrity sha512-PRuFlBBkvdDOtxlIASzTmkEFar+S66Ek48NVVTWMUjtJAdn5vyMSN8y6IZIoIymGpR36q2nZbIYazBWyFxL+IQ==
 | 
			
		||||
 | 
			
		||||
"@types/diff-match-patch@^1.0.32":
 | 
			
		||||
  version "1.0.32"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.32.tgz#d9c3b8c914aa8229485351db4865328337a3d09f"
 | 
			
		||||
  integrity sha512-bPYT5ECFiblzsVzyURaNhljBH2Gh1t9LowgUwciMrNAhFewLkHT2H0Mto07Y4/3KCOGZHRQll3CTtQZ0X11D/A==
 | 
			
		||||
 | 
			
		||||
"@types/events@^3.0.0":
 | 
			
		||||
  version "3.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue