mirror of https://github.com/vector-im/riot-web
				
				
				
			Remove all usages of slate in favour of CIDER
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>pull/21833/head
							parent
							
								
									92fb6fcdc2
								
							
						
					
					
						commit
						d31c863563
					
				| 
						 | 
				
			
			@ -33,7 +33,6 @@ src/components/views/rooms/RoomList.js
 | 
			
		|||
src/components/views/rooms/RoomPreviewBar.js
 | 
			
		||||
src/components/views/rooms/SearchBar.js
 | 
			
		||||
src/components/views/rooms/SearchResultTile.js
 | 
			
		||||
src/components/views/rooms/SlateMessageComposer.js
 | 
			
		||||
src/components/views/settings/ChangeAvatar.js
 | 
			
		||||
src/components/views/settings/ChangePassword.js
 | 
			
		||||
src/components/views/settings/DevicesPanel.js
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,10 +103,6 @@
 | 
			
		|||
    "react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594",
 | 
			
		||||
    "resize-observer-polyfill": "^1.5.0",
 | 
			
		||||
    "sanitize-html": "^1.18.4",
 | 
			
		||||
    "slate": "^0.41.2",
 | 
			
		||||
    "slate-html-serializer": "^0.6.1",
 | 
			
		||||
    "slate-md-serializer": "github:matrix-org/slate-md-serializer#f7c4ad3",
 | 
			
		||||
    "slate-react": "^0.18.10",
 | 
			
		||||
    "text-encoding-utf-8": "^1.0.1",
 | 
			
		||||
    "url": "^0.11.0",
 | 
			
		||||
    "velocity-animate": "^1.5.2",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,86 +0,0 @@
 | 
			
		|||
//@flow
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 Aviral Dasgupta
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import {Value} from 'slate';
 | 
			
		||||
 | 
			
		||||
import _clamp from 'lodash/clamp';
 | 
			
		||||
 | 
			
		||||
type MessageFormat = 'rich' | 'markdown';
 | 
			
		||||
 | 
			
		||||
class HistoryItem {
 | 
			
		||||
    // We store history items in their native format to ensure history is accurate
 | 
			
		||||
    // and then convert them if our RTE has subsequently changed format.
 | 
			
		||||
    value: Value;
 | 
			
		||||
    format: MessageFormat = 'rich';
 | 
			
		||||
 | 
			
		||||
    constructor(value: ?Value, format: ?MessageFormat) {
 | 
			
		||||
        this.value = value;
 | 
			
		||||
        this.format = format;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static fromJSON(obj: Object): HistoryItem {
 | 
			
		||||
        return new HistoryItem(
 | 
			
		||||
            Value.fromJSON(obj.value),
 | 
			
		||||
            obj.format,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toJSON(): Object {
 | 
			
		||||
        return {
 | 
			
		||||
            value: this.value.toJSON(),
 | 
			
		||||
            format: this.format,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class SlateComposerHistoryManager {
 | 
			
		||||
    history: Array<HistoryItem> = [];
 | 
			
		||||
    prefix: string;
 | 
			
		||||
    lastIndex: number = 0; // used for indexing the storage
 | 
			
		||||
    currentIndex: number = 0; // used for indexing the loaded validated history Array
 | 
			
		||||
 | 
			
		||||
    constructor(roomId: string, prefix: string = 'mx_composer_history_') {
 | 
			
		||||
        this.prefix = prefix + roomId;
 | 
			
		||||
 | 
			
		||||
        // TODO: Performance issues?
 | 
			
		||||
        let item;
 | 
			
		||||
        for (; item = sessionStorage.getItem(`${this.prefix}[${this.currentIndex}]`); this.currentIndex++) {
 | 
			
		||||
            try {
 | 
			
		||||
                this.history.push(
 | 
			
		||||
                    HistoryItem.fromJSON(JSON.parse(item)),
 | 
			
		||||
                );
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                console.warn("Throwing away unserialisable history", e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.lastIndex = this.currentIndex;
 | 
			
		||||
        // reset currentIndex to account for any unserialisable history
 | 
			
		||||
        this.currentIndex = this.history.length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    save(value: Value, format: MessageFormat) {
 | 
			
		||||
        const item = new HistoryItem(value, format);
 | 
			
		||||
        this.history.push(item);
 | 
			
		||||
        this.currentIndex = this.history.length;
 | 
			
		||||
        sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item.toJSON()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getItem(offset: number): ?HistoryItem {
 | 
			
		||||
        this.currentIndex = _clamp(this.currentIndex + offset, 0, this.history.length - 1);
 | 
			
		||||
        return this.history[this.currentIndex];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,92 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2018 New Vector Ltd
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Based originally on slate-plain-serializer
 | 
			
		||||
 | 
			
		||||
import { Block } from 'slate';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Plain text serializer, which converts a Slate `value` to a plain text string,
 | 
			
		||||
 * serializing pills into various different formats as required.
 | 
			
		||||
 *
 | 
			
		||||
 * @type {PlainWithPillsSerializer}
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class PlainWithPillsSerializer {
 | 
			
		||||
    /*
 | 
			
		||||
     * @param {String} options.pillFormat - either 'md', 'plain', 'id'
 | 
			
		||||
     */
 | 
			
		||||
    constructor(options = {}) {
 | 
			
		||||
        const {
 | 
			
		||||
            pillFormat = 'plain',
 | 
			
		||||
        } = options;
 | 
			
		||||
        this.pillFormat = pillFormat;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Serialize a Slate `value` to a plain text string,
 | 
			
		||||
     * serializing pills as either MD links, plain text representations or
 | 
			
		||||
     * ID representations as required.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {Value} value
 | 
			
		||||
     * @return {String}
 | 
			
		||||
     */
 | 
			
		||||
    serialize = value => {
 | 
			
		||||
        return this._serializeNode(value.document);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Serialize a `node` to plain text.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {Node} node
 | 
			
		||||
     * @return {String}
 | 
			
		||||
     */
 | 
			
		||||
    _serializeNode = node => {
 | 
			
		||||
        if (
 | 
			
		||||
            node.object == 'document' ||
 | 
			
		||||
            (node.object == 'block' && Block.isBlockList(node.nodes))
 | 
			
		||||
        ) {
 | 
			
		||||
            return node.nodes.map(this._serializeNode).join('\n');
 | 
			
		||||
        } else if (node.type == 'emoji') {
 | 
			
		||||
            return node.data.get('emojiUnicode');
 | 
			
		||||
        } else if (node.type == 'pill') {
 | 
			
		||||
            const completion = node.data.get('completion');
 | 
			
		||||
            // over the wire the @room pill is just plaintext
 | 
			
		||||
            if (completion === '@room') return completion;
 | 
			
		||||
 | 
			
		||||
            switch (this.pillFormat) {
 | 
			
		||||
                case 'plain':
 | 
			
		||||
                    return completion;
 | 
			
		||||
                case 'md':
 | 
			
		||||
                    return `[${ completion }](${ node.data.get('href') })`;
 | 
			
		||||
                case 'id':
 | 
			
		||||
                    return node.data.get('completionId') || completion;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (node.nodes) {
 | 
			
		||||
            return node.nodes.map(this._serializeNode).join('');
 | 
			
		||||
        } else {
 | 
			
		||||
            return node.text;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Export.
 | 
			
		||||
 *
 | 
			
		||||
 * @type {PlainWithPillsSerializer}
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export default PlainWithPillsSerializer;
 | 
			
		||||
| 
						 | 
				
			
			@ -393,13 +393,6 @@ const LoggedInView = createReactClass({
 | 
			
		|||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // XXX: Remove after CIDER replaces Slate completely: https://github.com/vector-im/riot-web/issues/11036
 | 
			
		||||
            // If using Slate, consume the Backspace without first focusing as it causes an implosion
 | 
			
		||||
            if (ev.key === Key.BACKSPACE && !SettingsStore.getValue("useCiderComposer")) {
 | 
			
		||||
                ev.stopPropagation();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!isClickShortcut && ev.key !== Key.TAB && !canElementReceiveInput(ev.target)) {
 | 
			
		||||
                // synchronous dispatch so we focus before key generates input
 | 
			
		||||
                dis.dispatch({action: 'focus_composer'}, true);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,8 +157,6 @@ module.exports = createReactClass({
 | 
			
		|||
 | 
			
		||||
            canReact: false,
 | 
			
		||||
            canReply: false,
 | 
			
		||||
 | 
			
		||||
            useCider: false,
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -180,18 +178,10 @@ module.exports = createReactClass({
 | 
			
		|||
 | 
			
		||||
        WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate);
 | 
			
		||||
 | 
			
		||||
        this._onCiderUpdated();
 | 
			
		||||
        this._ciderWatcherRef = SettingsStore.watchSetting(
 | 
			
		||||
            "useCiderComposer", null, this._onCiderUpdated);
 | 
			
		||||
 | 
			
		||||
        this._roomView = createRef();
 | 
			
		||||
        this._searchResultsPanel = createRef();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCiderUpdated: function() {
 | 
			
		||||
        this.setState({useCider: SettingsStore.getValue("useCiderComposer")});
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onRoomViewStoreUpdate: function(initial) {
 | 
			
		||||
        if (this.unmounted) {
 | 
			
		||||
            return;
 | 
			
		||||
| 
						 | 
				
			
			@ -1806,29 +1796,16 @@ module.exports = createReactClass({
 | 
			
		|||
            myMembership === 'join' && !this.state.searchResults
 | 
			
		||||
        );
 | 
			
		||||
        if (canSpeak) {
 | 
			
		||||
            if (this.state.useCider) {
 | 
			
		||||
                const MessageComposer = sdk.getComponent('rooms.MessageComposer');
 | 
			
		||||
                messageComposer =
 | 
			
		||||
                    <MessageComposer
 | 
			
		||||
                        room={this.state.room}
 | 
			
		||||
                        callState={this.state.callState}
 | 
			
		||||
                        disabled={this.props.disabled}
 | 
			
		||||
                        showApps={this.state.showApps}
 | 
			
		||||
                        e2eStatus={this.state.e2eStatus}
 | 
			
		||||
                        permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
 | 
			
		||||
                    />;
 | 
			
		||||
            } else {
 | 
			
		||||
                const SlateMessageComposer = sdk.getComponent('rooms.SlateMessageComposer');
 | 
			
		||||
                messageComposer =
 | 
			
		||||
                    <SlateMessageComposer
 | 
			
		||||
                        room={this.state.room}
 | 
			
		||||
                        callState={this.state.callState}
 | 
			
		||||
                        disabled={this.props.disabled}
 | 
			
		||||
                        showApps={this.state.showApps}
 | 
			
		||||
                        e2eStatus={this.state.e2eStatus}
 | 
			
		||||
                        permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
 | 
			
		||||
                    />;
 | 
			
		||||
            }
 | 
			
		||||
            const MessageComposer = sdk.getComponent('rooms.MessageComposer');
 | 
			
		||||
            messageComposer =
 | 
			
		||||
                <MessageComposer
 | 
			
		||||
                    room={this.state.room}
 | 
			
		||||
                    callState={this.state.callState}
 | 
			
		||||
                    disabled={this.props.disabled}
 | 
			
		||||
                    showApps={this.state.showApps}
 | 
			
		||||
                    e2eStatus={this.state.e2eStatus}
 | 
			
		||||
                    permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
 | 
			
		||||
                />;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO: Why aren't we storing the term/scope/count in this format
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,485 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2015, 2016 OpenMarket Ltd
 | 
			
		||||
Copyright 2017, 2018 New Vector Ltd
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
import React, {createRef} from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { _t, _td } from '../../../languageHandler';
 | 
			
		||||
import CallHandler from '../../../CallHandler';
 | 
			
		||||
import MatrixClientPeg from '../../../MatrixClientPeg';
 | 
			
		||||
import sdk from '../../../index';
 | 
			
		||||
import dis from '../../../dispatcher';
 | 
			
		||||
import RoomViewStore from '../../../stores/RoomViewStore';
 | 
			
		||||
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
 | 
			
		||||
import Stickerpicker from './Stickerpicker';
 | 
			
		||||
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
 | 
			
		||||
import ContentMessages from '../../../ContentMessages';
 | 
			
		||||
 | 
			
		||||
import E2EIcon from './E2EIcon';
 | 
			
		||||
 | 
			
		||||
const formatButtonList = [
 | 
			
		||||
    _td("bold"),
 | 
			
		||||
    _td("italic"),
 | 
			
		||||
    _td("deleted"),
 | 
			
		||||
    _td("underlined"),
 | 
			
		||||
    _td("inline-code"),
 | 
			
		||||
    _td("block-quote"),
 | 
			
		||||
    _td("bulleted-list"),
 | 
			
		||||
    _td("numbered-list"),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
function ComposerAvatar(props) {
 | 
			
		||||
    const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
 | 
			
		||||
    return <div className="mx_MessageComposer_avatar">
 | 
			
		||||
        <MemberStatusMessageAvatar member={props.me} width={24} height={24} />
 | 
			
		||||
    </div>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComposerAvatar.propTypes = {
 | 
			
		||||
    me: PropTypes.object.isRequired,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function CallButton(props) {
 | 
			
		||||
    const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
 | 
			
		||||
    const onVoiceCallClick = (ev) => {
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: 'place_call',
 | 
			
		||||
            type: "voice",
 | 
			
		||||
            room_id: props.roomId,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return <AccessibleButton className="mx_MessageComposer_button mx_MessageComposer_voicecall"
 | 
			
		||||
        onClick={onVoiceCallClick}
 | 
			
		||||
        title={_t('Voice call')}
 | 
			
		||||
    />
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CallButton.propTypes = {
 | 
			
		||||
    roomId: PropTypes.string.isRequired
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function VideoCallButton(props) {
 | 
			
		||||
    const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
 | 
			
		||||
    const onCallClick = (ev) => {
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: 'place_call',
 | 
			
		||||
            type: ev.shiftKey ? "screensharing" : "video",
 | 
			
		||||
            room_id: props.roomId,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return <AccessibleButton className="mx_MessageComposer_button mx_MessageComposer_videocall"
 | 
			
		||||
        onClick={onCallClick}
 | 
			
		||||
        title={_t('Video call')}
 | 
			
		||||
    />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoCallButton.propTypes = {
 | 
			
		||||
    roomId: PropTypes.string.isRequired,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function HangupButton(props) {
 | 
			
		||||
    const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
 | 
			
		||||
    const onHangupClick = () => {
 | 
			
		||||
        const call = CallHandler.getCallForRoom(props.roomId);
 | 
			
		||||
        if (!call) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: 'hangup',
 | 
			
		||||
            // hangup the call for this room, which may not be the room in props
 | 
			
		||||
            // (e.g. conferences which will hangup the 1:1 room instead)
 | 
			
		||||
            room_id: call.roomId,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    return  <AccessibleButton className="mx_MessageComposer_button mx_MessageComposer_hangup"
 | 
			
		||||
        onClick={onHangupClick}
 | 
			
		||||
        title={_t('Hangup')}
 | 
			
		||||
    />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HangupButton.propTypes = {
 | 
			
		||||
    roomId: PropTypes.string.isRequired,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function FormattingButton(props) {
 | 
			
		||||
    const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
 | 
			
		||||
    return <AccessibleButton
 | 
			
		||||
        element="img"
 | 
			
		||||
        className="mx_MessageComposer_formatting"
 | 
			
		||||
        alt={_t("Show Text Formatting Toolbar")}
 | 
			
		||||
        title={_t("Show Text Formatting Toolbar")}
 | 
			
		||||
        src={require("../../../../res/img/button-text-formatting.svg")}
 | 
			
		||||
        style={{visibility: props.showFormatting ? 'hidden' : 'visible'}}
 | 
			
		||||
        onClick={props.onClickHandler}
 | 
			
		||||
    />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FormattingButton.propTypes = {
 | 
			
		||||
    showFormatting: PropTypes.bool.isRequired,
 | 
			
		||||
    onClickHandler: PropTypes.func.isRequired,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class UploadButton extends React.Component {
 | 
			
		||||
    static propTypes = {
 | 
			
		||||
        roomId: PropTypes.string.isRequired,
 | 
			
		||||
    }
 | 
			
		||||
    constructor(props) {
 | 
			
		||||
        super(props);
 | 
			
		||||
        this.onUploadClick = this.onUploadClick.bind(this);
 | 
			
		||||
        this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
 | 
			
		||||
 | 
			
		||||
        this._uploadInput = createRef();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onUploadClick(ev) {
 | 
			
		||||
        if (MatrixClientPeg.get().isGuest()) {
 | 
			
		||||
            dis.dispatch({action: 'require_registration'});
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this._uploadInput.current.click();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onUploadFileInputChange(ev) {
 | 
			
		||||
        if (ev.target.files.length === 0) return;
 | 
			
		||||
 | 
			
		||||
        // take a copy so we can safely reset the value of the form control
 | 
			
		||||
        // (Note it is a FileList: we can't use slice or sesnible iteration).
 | 
			
		||||
        const tfiles = [];
 | 
			
		||||
        for (let i = 0; i < ev.target.files.length; ++i) {
 | 
			
		||||
            tfiles.push(ev.target.files[i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ContentMessages.sharedInstance().sendContentListToRoom(
 | 
			
		||||
            tfiles, this.props.roomId, MatrixClientPeg.get(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // This is the onChange handler for a file form control, but we're
 | 
			
		||||
        // not keeping any state, so reset the value of the form control
 | 
			
		||||
        // to empty.
 | 
			
		||||
        // NB. we need to set 'value': the 'files' property is immutable.
 | 
			
		||||
        ev.target.value = '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        const uploadInputStyle = {display: 'none'};
 | 
			
		||||
        const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
 | 
			
		||||
        return (
 | 
			
		||||
            <AccessibleButton className="mx_MessageComposer_button mx_MessageComposer_upload"
 | 
			
		||||
                onClick={this.onUploadClick}
 | 
			
		||||
                title={_t('Upload file')}
 | 
			
		||||
            >
 | 
			
		||||
                <input ref={this._uploadInput} type="file"
 | 
			
		||||
                    style={uploadInputStyle}
 | 
			
		||||
                    multiple
 | 
			
		||||
                    onChange={this.onUploadFileInputChange}
 | 
			
		||||
                />
 | 
			
		||||
            </AccessibleButton>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class SlateMessageComposer extends React.Component {
 | 
			
		||||
    constructor(props) {
 | 
			
		||||
        super(props);
 | 
			
		||||
        this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
 | 
			
		||||
        this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
 | 
			
		||||
        this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
 | 
			
		||||
        this.onInputStateChanged = this.onInputStateChanged.bind(this);
 | 
			
		||||
        this.onEvent = this.onEvent.bind(this);
 | 
			
		||||
        this._onRoomStateEvents = this._onRoomStateEvents.bind(this);
 | 
			
		||||
        this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);
 | 
			
		||||
        this._onTombstoneClick = this._onTombstoneClick.bind(this);
 | 
			
		||||
        this.renderPlaceholderText = this.renderPlaceholderText.bind(this);
 | 
			
		||||
        this.renderFormatBar = this.renderFormatBar.bind(this);
 | 
			
		||||
 | 
			
		||||
        this.state = {
 | 
			
		||||
            inputState: {
 | 
			
		||||
                marks: [],
 | 
			
		||||
                blockType: null,
 | 
			
		||||
                isRichTextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'),
 | 
			
		||||
            },
 | 
			
		||||
            showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
 | 
			
		||||
            isQuoting: Boolean(RoomViewStore.getQuotingEvent()),
 | 
			
		||||
            tombstone: this._getRoomTombstone(),
 | 
			
		||||
            canSendMessages: this.props.room.maySendMessage(),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    componentDidMount() {
 | 
			
		||||
        // N.B. using 'event' rather than 'RoomEvents' otherwise the crypto handler
 | 
			
		||||
        // for 'event' fires *after* 'RoomEvent', and our room won't have yet been
 | 
			
		||||
        // marked as encrypted.
 | 
			
		||||
        // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
 | 
			
		||||
        MatrixClientPeg.get().on("event", this.onEvent);
 | 
			
		||||
        MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents);
 | 
			
		||||
        this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
 | 
			
		||||
        this._waitForOwnMember();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _waitForOwnMember() {
 | 
			
		||||
        // if we have the member already, do that
 | 
			
		||||
        const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
 | 
			
		||||
        if (me) {
 | 
			
		||||
            this.setState({me});
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // Otherwise, wait for member loading to finish and then update the member for the avatar.
 | 
			
		||||
        // The members should already be loading, and loadMembersIfNeeded
 | 
			
		||||
        // will return the promise for the existing operation
 | 
			
		||||
        this.props.room.loadMembersIfNeeded().then(() => {
 | 
			
		||||
            const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
 | 
			
		||||
            this.setState({me});
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    componentWillUnmount() {
 | 
			
		||||
        if (MatrixClientPeg.get()) {
 | 
			
		||||
            MatrixClientPeg.get().removeListener("event", this.onEvent);
 | 
			
		||||
            MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents);
 | 
			
		||||
        }
 | 
			
		||||
        if (this._roomStoreToken) {
 | 
			
		||||
            this._roomStoreToken.remove();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onEvent(event) {
 | 
			
		||||
        if (event.getType() !== 'm.room.encryption') return;
 | 
			
		||||
        if (event.getRoomId() !== this.props.room.roomId) return;
 | 
			
		||||
        this.forceUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onRoomStateEvents(ev, state) {
 | 
			
		||||
        if (ev.getRoomId() !== this.props.room.roomId) return;
 | 
			
		||||
 | 
			
		||||
        if (ev.getType() === 'm.room.tombstone') {
 | 
			
		||||
            this.setState({tombstone: this._getRoomTombstone()});
 | 
			
		||||
        }
 | 
			
		||||
        if (ev.getType() === 'm.room.power_levels') {
 | 
			
		||||
            this.setState({canSendMessages: this.props.room.maySendMessage()});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRoomTombstone() {
 | 
			
		||||
        return this.props.room.currentState.getStateEvents('m.room.tombstone', '');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onRoomViewStoreUpdate() {
 | 
			
		||||
        const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
 | 
			
		||||
        if (this.state.isQuoting === isQuoting) return;
 | 
			
		||||
        this.setState({ isQuoting });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    onInputStateChanged(inputState) {
 | 
			
		||||
        // Merge the new input state with old to support partial updates
 | 
			
		||||
        inputState = Object.assign({}, this.state.inputState, inputState);
 | 
			
		||||
        this.setState({inputState});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onAutocompleteConfirm(range, completion) {
 | 
			
		||||
        if (this.messageComposerInput) {
 | 
			
		||||
            this.messageComposerInput.setDisplayedCompletion(range, completion);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onFormatButtonClicked(name, event) {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        this.messageComposerInput.onFormatButtonClicked(name, event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onToggleFormattingClicked() {
 | 
			
		||||
        SettingsStore.setValue("MessageComposer.showFormatting", null, SettingLevel.DEVICE, !this.state.showFormatting);
 | 
			
		||||
        this.setState({showFormatting: !this.state.showFormatting});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onToggleMarkdownClicked(e) {
 | 
			
		||||
        e.preventDefault(); // don't steal focus from the editor!
 | 
			
		||||
        this.messageComposerInput.enableRichtext(!this.state.inputState.isRichTextEnabled);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onTombstoneClick(ev) {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
 | 
			
		||||
        const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
 | 
			
		||||
        const replacementRoom = MatrixClientPeg.get().getRoom(replacementRoomId);
 | 
			
		||||
        let createEventId = null;
 | 
			
		||||
        if (replacementRoom) {
 | 
			
		||||
            const createEvent = replacementRoom.currentState.getStateEvents('m.room.create', '');
 | 
			
		||||
            if (createEvent && createEvent.getId()) createEventId = createEvent.getId();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const viaServers = [this.state.tombstone.getSender().split(':').splice(1).join(':')];
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: 'view_room',
 | 
			
		||||
            highlighted: true,
 | 
			
		||||
            event_id: createEventId,
 | 
			
		||||
            room_id: replacementRoomId,
 | 
			
		||||
            auto_join: true,
 | 
			
		||||
 | 
			
		||||
            // Try to join via the server that sent the event. This converts @something:example.org
 | 
			
		||||
            // into a server domain by splitting on colons and ignoring the first entry ("@something").
 | 
			
		||||
            via_servers: viaServers,
 | 
			
		||||
            opts: {
 | 
			
		||||
                // These are passed down to the js-sdk's /join call
 | 
			
		||||
                viaServers: viaServers,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderPlaceholderText() {
 | 
			
		||||
        const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
 | 
			
		||||
        if (this.state.isQuoting) {
 | 
			
		||||
            if (roomIsEncrypted) {
 | 
			
		||||
                return _t('Send an encrypted reply…');
 | 
			
		||||
            } else {
 | 
			
		||||
                return _t('Send a reply (unencrypted)…');
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (roomIsEncrypted) {
 | 
			
		||||
                return _t('Send an encrypted message…');
 | 
			
		||||
            } else {
 | 
			
		||||
                return _t('Send a message (unencrypted)…');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderFormatBar() {
 | 
			
		||||
        const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
 | 
			
		||||
        const {marks, blockType} = this.state.inputState;
 | 
			
		||||
        const formatButtons = formatButtonList.map((name) => {
 | 
			
		||||
            // special-case to match the md serializer and the special-case in MessageComposerInput.js
 | 
			
		||||
            const markName = name === 'inline-code' ? 'code' : name;
 | 
			
		||||
            const active = marks.some(mark => mark.type === markName) || blockType === name;
 | 
			
		||||
            const suffix = active ? '-on' : '';
 | 
			
		||||
            const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
 | 
			
		||||
            const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
 | 
			
		||||
            return (
 | 
			
		||||
                <img className={className}
 | 
			
		||||
                    title={_t(name)}
 | 
			
		||||
                    onMouseDown={onFormatButtonClicked}
 | 
			
		||||
                    key={name}
 | 
			
		||||
                    src={require(`../../../../res/img/button-text-${name}${suffix}.svg`)}
 | 
			
		||||
                    height="17"
 | 
			
		||||
                />
 | 
			
		||||
            );
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            <div className="mx_MessageComposer_formatbar_wrapper">
 | 
			
		||||
                <div className="mx_MessageComposer_formatbar">
 | 
			
		||||
                { formatButtons }
 | 
			
		||||
                <div style={{ flex: 1 }}></div>
 | 
			
		||||
                <AccessibleButton
 | 
			
		||||
                    className="mx_MessageComposer_formatbar_markdown mx_MessageComposer_markdownDisabled"
 | 
			
		||||
                    onClick={this.onToggleMarkdownClicked}
 | 
			
		||||
                    title={_t("Markdown is disabled")}
 | 
			
		||||
                />
 | 
			
		||||
                <AccessibleButton element="img" title={_t("Hide Text Formatting Toolbar")}
 | 
			
		||||
                    onClick={this.onToggleFormattingClicked}
 | 
			
		||||
                    className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
 | 
			
		||||
                    src={require("../../../../res/img/icon-text-cancel.svg")}
 | 
			
		||||
                />
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        const controls = [
 | 
			
		||||
            this.state.me ? <ComposerAvatar key="controls_avatar" me={this.state.me} /> : null,
 | 
			
		||||
            this.props.e2eStatus ? <E2EIcon key="e2eIcon" status={this.props.e2eStatus} className="mx_MessageComposer_e2eIcon" /> : null,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if (!this.state.tombstone && this.state.canSendMessages) {
 | 
			
		||||
            // This also currently includes the call buttons. Really we should
 | 
			
		||||
            // check separately for whether we can call, but this is slightly
 | 
			
		||||
            // complex because of conference calls.
 | 
			
		||||
 | 
			
		||||
            const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");
 | 
			
		||||
            const showFormattingButton = this.state.inputState.isRichTextEnabled;
 | 
			
		||||
            const callInProgress = this.props.callState && this.props.callState !== 'ended';
 | 
			
		||||
 | 
			
		||||
            controls.push(
 | 
			
		||||
                <MessageComposerInput
 | 
			
		||||
                    ref={(c) => this.messageComposerInput = c}
 | 
			
		||||
                    key="controls_input"
 | 
			
		||||
                    room={this.props.room}
 | 
			
		||||
                    placeholder={this.renderPlaceholderText()}
 | 
			
		||||
                    onInputStateChanged={this.onInputStateChanged}
 | 
			
		||||
                    permalinkCreator={this.props.permalinkCreator} />,
 | 
			
		||||
                showFormattingButton ? <FormattingButton key="controls_formatting"
 | 
			
		||||
                    showFormatting={this.state.showFormatting} onClickHandler={this.onToggleFormattingClicked} /> : null,
 | 
			
		||||
                <Stickerpicker key='stickerpicker_controls_button' room={this.props.room} />,
 | 
			
		||||
                <UploadButton key="controls_upload" roomId={this.props.room.roomId} />,
 | 
			
		||||
                callInProgress ? <HangupButton key="controls_hangup" roomId={this.props.room.roomId} /> : null,
 | 
			
		||||
                callInProgress ? null : <CallButton key="controls_call" roomId={this.props.room.roomId} />,
 | 
			
		||||
                callInProgress ? null : <VideoCallButton key="controls_videocall" roomId={this.props.room.roomId} />,
 | 
			
		||||
            );
 | 
			
		||||
        } else if (this.state.tombstone) {
 | 
			
		||||
            const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
 | 
			
		||||
 | 
			
		||||
            const continuesLink = replacementRoomId ? (
 | 
			
		||||
                <a href={makeRoomPermalink(replacementRoomId)}
 | 
			
		||||
                    className="mx_MessageComposer_roomReplaced_link"
 | 
			
		||||
                    onClick={this._onTombstoneClick}
 | 
			
		||||
                >
 | 
			
		||||
                    {_t("The conversation continues here.")}
 | 
			
		||||
                </a>
 | 
			
		||||
            ) : '';
 | 
			
		||||
 | 
			
		||||
            controls.push(<div className="mx_MessageComposer_replaced_wrapper">
 | 
			
		||||
                <div className="mx_MessageComposer_replaced_valign">
 | 
			
		||||
                    <img className="mx_MessageComposer_roomReplaced_icon" src={require("../../../../res/img/room_replaced.svg")} />
 | 
			
		||||
                    <span className="mx_MessageComposer_roomReplaced_header">
 | 
			
		||||
                        {_t("This room has been replaced and is no longer active.")}
 | 
			
		||||
                    </span><br />
 | 
			
		||||
                    { continuesLink }
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>);
 | 
			
		||||
        } else {
 | 
			
		||||
            controls.push(
 | 
			
		||||
                <div key="controls_error" className="mx_MessageComposer_noperm_error">
 | 
			
		||||
                    { _t('You do not have permission to post to this room') }
 | 
			
		||||
                </div>,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const showFormatBar = this.state.showFormatting && this.state.inputState.isRichTextEnabled;
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            <div className="mx_MessageComposer">
 | 
			
		||||
                <div className="mx_MessageComposer_wrapper">
 | 
			
		||||
                    <div className="mx_MessageComposer_row">
 | 
			
		||||
                        { controls }
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                { showFormatBar ? this.renderFormatBar() : null }
 | 
			
		||||
            </div>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SlateMessageComposer.propTypes = {
 | 
			
		||||
    // js-sdk Room object
 | 
			
		||||
    room: PropTypes.object.isRequired,
 | 
			
		||||
 | 
			
		||||
    // string representing the current voip call state
 | 
			
		||||
    callState: PropTypes.string,
 | 
			
		||||
 | 
			
		||||
    // string representing the current room app drawer state
 | 
			
		||||
    showApps: PropTypes.bool
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,6 @@ import PlatformPeg from "../../../../../PlatformPeg";
 | 
			
		|||
 | 
			
		||||
export default class PreferencesUserSettingsTab extends React.Component {
 | 
			
		||||
    static COMPOSER_SETTINGS = [
 | 
			
		||||
        'useCiderComposer',
 | 
			
		||||
        'MessageComposerInput.autoReplaceEmoji',
 | 
			
		||||
        'MessageComposerInput.suggestEmoji',
 | 
			
		||||
        'sendTypingNotifications',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -362,7 +362,6 @@
 | 
			
		|||
    "Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)",
 | 
			
		||||
    "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)",
 | 
			
		||||
    "Show info about bridges in room settings": "Show info about bridges in room settings",
 | 
			
		||||
    "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages",
 | 
			
		||||
    "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing",
 | 
			
		||||
    "Use compact timeline layout": "Use compact timeline layout",
 | 
			
		||||
    "Show a placeholder for removed messages": "Show a placeholder for removed messages",
 | 
			
		||||
| 
						 | 
				
			
			@ -956,13 +955,6 @@
 | 
			
		|||
    "Strikethrough": "Strikethrough",
 | 
			
		||||
    "Code block": "Code block",
 | 
			
		||||
    "Quote": "Quote",
 | 
			
		||||
    "Server error": "Server error",
 | 
			
		||||
    "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
 | 
			
		||||
    "Command error": "Command error",
 | 
			
		||||
    "Unable to reply": "Unable to reply",
 | 
			
		||||
    "At this time it is not possible to reply with an emote.": "At this time it is not possible to reply with an emote.",
 | 
			
		||||
    "Markdown is disabled": "Markdown is disabled",
 | 
			
		||||
    "Markdown is enabled": "Markdown is enabled",
 | 
			
		||||
    "No pinned messages.": "No pinned messages.",
 | 
			
		||||
    "Loading...": "Loading...",
 | 
			
		||||
    "Pinned Messages": "Pinned Messages",
 | 
			
		||||
| 
						 | 
				
			
			@ -1064,16 +1056,9 @@
 | 
			
		|||
    "This Room": "This Room",
 | 
			
		||||
    "All Rooms": "All Rooms",
 | 
			
		||||
    "Search…": "Search…",
 | 
			
		||||
    "bold": "bold",
 | 
			
		||||
    "italic": "italic",
 | 
			
		||||
    "deleted": "deleted",
 | 
			
		||||
    "underlined": "underlined",
 | 
			
		||||
    "inline-code": "inline-code",
 | 
			
		||||
    "block-quote": "block-quote",
 | 
			
		||||
    "bulleted-list": "bulleted-list",
 | 
			
		||||
    "numbered-list": "numbered-list",
 | 
			
		||||
    "Show Text Formatting Toolbar": "Show Text Formatting Toolbar",
 | 
			
		||||
    "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar",
 | 
			
		||||
    "Server error": "Server error",
 | 
			
		||||
    "Command error": "Command error",
 | 
			
		||||
    "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
 | 
			
		||||
    "Failed to connect to integration manager": "Failed to connect to integration manager",
 | 
			
		||||
    "You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled",
 | 
			
		||||
    "Add some now": "Add some now",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -161,11 +161,6 @@ export const SETTINGS = {
 | 
			
		|||
        displayName: _td("Show info about bridges in room settings"),
 | 
			
		||||
        default: false,
 | 
			
		||||
    },
 | 
			
		||||
    "useCiderComposer": {
 | 
			
		||||
        displayName: _td("Use the new, faster, composer for writing messages"),
 | 
			
		||||
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
 | 
			
		||||
        default: true,
 | 
			
		||||
    },
 | 
			
		||||
    "MessageComposerInput.suggestEmoji": {
 | 
			
		||||
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
 | 
			
		||||
        displayName: _td('Enable Emoji suggestions while typing'),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,66 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2017, 2018 Vector Creations Ltd
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
import { Value } from 'slate';
 | 
			
		||||
 | 
			
		||||
const localStoragePrefix = 'editor_state_';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A class for storing application state to do with the message composer (specifically in-progress message drafts).
 | 
			
		||||
 * It does not worry about cleaning up on log out as this is handled in Lifecycle.js by localStorage.clear()
 | 
			
		||||
 */
 | 
			
		||||
class MessageComposerStore {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.prefix = localStoragePrefix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getKey(roomId: string): string {
 | 
			
		||||
        return this.prefix + roomId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setEditorState(roomId: string, editorState: Value, richText: boolean) {
 | 
			
		||||
        localStorage.setItem(this._getKey(roomId), JSON.stringify({
 | 
			
		||||
            editor_state: editorState.toJSON({
 | 
			
		||||
                preserveSelection: true,
 | 
			
		||||
                // XXX: re-hydrating history is not currently supported by fromJSON
 | 
			
		||||
                // preserveHistory: true,
 | 
			
		||||
                // XXX: this seems like a workaround for selection.isSet being based on anchorKey instead of anchorPath
 | 
			
		||||
                preserveKeys: true,
 | 
			
		||||
            }),
 | 
			
		||||
            rich_text: richText,
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getEditorState(roomId): {editor_state: Value, rich_text: boolean} {
 | 
			
		||||
        const stateStr = localStorage.getItem(this._getKey(roomId));
 | 
			
		||||
 | 
			
		||||
        let state;
 | 
			
		||||
        if (stateStr) {
 | 
			
		||||
            state = JSON.parse(stateStr);
 | 
			
		||||
 | 
			
		||||
            // if it does not have the fields we expect then bail
 | 
			
		||||
            if (!state || state.rich_text === undefined || state.editor_state === undefined) return;
 | 
			
		||||
            state.editor_state = Value.fromJSON(state.editor_state);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return state;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let singletonMessageComposerStore = null;
 | 
			
		||||
if (!singletonMessageComposerStore) {
 | 
			
		||||
    singletonMessageComposerStore = new MessageComposerStore();
 | 
			
		||||
}
 | 
			
		||||
module.exports = singletonMessageComposerStore;
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ function addTextToDraft(text) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FIXME: These tests need to be updated from Draft to Slate.
 | 
			
		||||
// FIXME: These tests need to be updated from Draft to CIDER.
 | 
			
		||||
 | 
			
		||||
xdescribe('MessageComposerInput', () => {
 | 
			
		||||
    let parentDiv = null,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										151
									
								
								yarn.lock
								
								
								
								
							
							
						
						
									
										151
									
								
								yarn.lock
								
								
								
								
							| 
						 | 
				
			
			@ -2592,11 +2592,6 @@ dir-glob@^2.2.2:
 | 
			
		|||
  dependencies:
 | 
			
		||||
    path-type "^3.0.0"
 | 
			
		||||
 | 
			
		||||
direction@^0.1.5:
 | 
			
		||||
  version "0.1.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/direction/-/direction-0.1.5.tgz#ce5d797f97e26f8be7beff53f7dc40e1c1a9ec4c"
 | 
			
		||||
  integrity sha1-zl15f5fib4vnvv9T99xA4cGp7Ew=
 | 
			
		||||
 | 
			
		||||
doctrine@^2.1.0:
 | 
			
		||||
  version "2.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
 | 
			
		||||
| 
						 | 
				
			
			@ -3065,11 +3060,6 @@ esrecurse@^4.1.0:
 | 
			
		|||
  dependencies:
 | 
			
		||||
    estraverse "^4.1.0"
 | 
			
		||||
 | 
			
		||||
esrever@^0.2.0:
 | 
			
		||||
  version "0.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/esrever/-/esrever-0.2.0.tgz#96e9d28f4f1b1a76784cd5d490eaae010e7407b8"
 | 
			
		||||
  integrity sha1-lunSj08bGnZ4TNXUkOquAQ50B7g=
 | 
			
		||||
 | 
			
		||||
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
 | 
			
		||||
  version "4.3.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
 | 
			
		||||
| 
						 | 
				
			
			@ -3653,11 +3643,6 @@ get-caller-file@^2.0.1:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
 | 
			
		||||
  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 | 
			
		||||
 | 
			
		||||
get-document@1:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-document/-/get-document-1.0.0.tgz#4821bce66f1c24cb0331602be6cb6b12c4f01c4b"
 | 
			
		||||
  integrity sha1-SCG85m8cJMsDMWAr5strEsTwHEs=
 | 
			
		||||
 | 
			
		||||
get-stdin@^6.0.0:
 | 
			
		||||
  version "6.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
 | 
			
		||||
| 
						 | 
				
			
			@ -3675,13 +3660,6 @@ get-value@^2.0.3, get-value@^2.0.6:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
 | 
			
		||||
  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
 | 
			
		||||
 | 
			
		||||
get-window@^1.1.1:
 | 
			
		||||
  version "1.1.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-window/-/get-window-1.1.2.tgz#65fbaa999fb87f86ea5d30770f4097707044f47f"
 | 
			
		||||
  integrity sha512-yjWpFcy9fjhLQHW1dPtg9ga4pmizLY8y4ZSHdGrAQ1NU277MRhnGnnLPxe19X8W5lWVsCZz++5xEuNozWMVmTw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    get-document "1"
 | 
			
		||||
 | 
			
		||||
getpass@^0.1.1:
 | 
			
		||||
  version "0.1.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
 | 
			
		||||
| 
						 | 
				
			
			@ -4509,16 +4487,6 @@ is-hexadecimal@^1.0.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz#e8a426a69b6d31470d3a33a47bb825cda02506ee"
 | 
			
		||||
  integrity sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA==
 | 
			
		||||
 | 
			
		||||
is-hotkey@0.1.4:
 | 
			
		||||
  version "0.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.4.tgz#c34d2c85d6ec8d09a871dcf71931c8067a824c7d"
 | 
			
		||||
  integrity sha512-Py+aW4r5mBBY18TGzGz286/gKS+fCQ0Hee3qkaiSmEPiD0PqFpe0wuA3l7rTOUKyeXl8Mxf3XzJxIoTlSv+kxA==
 | 
			
		||||
 | 
			
		||||
is-in-browser@^1.1.3:
 | 
			
		||||
  version "1.1.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
 | 
			
		||||
  integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=
 | 
			
		||||
 | 
			
		||||
is-ip@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab"
 | 
			
		||||
| 
						 | 
				
			
			@ -4651,11 +4619,6 @@ is-whitespace-character@^1.0.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz#b3ad9546d916d7d3ffa78204bca0c26b56257fac"
 | 
			
		||||
  integrity sha512-SNPgMLz9JzPccD3nPctcj8sZlX9DAMJSKH8bP7Z6bohCwuNgX8xbWr1eTAYXX9Vpi/aSn8Y1akL9WgM3t43YNQ==
 | 
			
		||||
 | 
			
		||||
is-window@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d"
 | 
			
		||||
  integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0=
 | 
			
		||||
 | 
			
		||||
is-windows@^1.0.1, is-windows@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
 | 
			
		||||
| 
						 | 
				
			
			@ -4715,11 +4678,6 @@ isobject@^3.0.0, isobject@^3.0.1:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
 | 
			
		||||
  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
 | 
			
		||||
 | 
			
		||||
isomorphic-base64@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/isomorphic-base64/-/isomorphic-base64-1.0.2.tgz#f426aae82569ba8a4ec5ca73ad21a44ab1ee7803"
 | 
			
		||||
  integrity sha1-9Caq6CVpuopOxcpzrSGkSrHueAM=
 | 
			
		||||
 | 
			
		||||
isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1:
 | 
			
		||||
  version "2.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
 | 
			
		||||
| 
						 | 
				
			
			@ -5154,7 +5112,7 @@ lodash.unescape@4.0.1:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
 | 
			
		||||
  integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
 | 
			
		||||
 | 
			
		||||
lodash@^4.1.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1:
 | 
			
		||||
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1:
 | 
			
		||||
  version "4.17.15"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
 | 
			
		||||
  integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
 | 
			
		||||
| 
						 | 
				
			
			@ -5379,11 +5337,6 @@ memoize-one@^3.0.1:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-3.1.1.tgz#ef609811e3bc28970eac2884eece64d167830d17"
 | 
			
		||||
  integrity sha512-YqVh744GsMlZu6xkhGslPSqSurOv6P+kLN2J3ysBZfagLcL5FdRK/0UpgLoL8hwjjEvvAVkjJZyFP+1T6p1vgA==
 | 
			
		||||
 | 
			
		||||
memoize-one@^4.0.0:
 | 
			
		||||
  version "4.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906"
 | 
			
		||||
  integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA==
 | 
			
		||||
 | 
			
		||||
memory-fs@^0.4.0, memory-fs@^0.4.1:
 | 
			
		||||
  version "0.4.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
 | 
			
		||||
| 
						 | 
				
			
			@ -6715,11 +6668,6 @@ react-focus-lock@^2.2.1:
 | 
			
		|||
  dependencies:
 | 
			
		||||
    gemini-scrollbar matrix-org/gemini-scrollbar#91e1e566
 | 
			
		||||
 | 
			
		||||
react-immutable-proptypes@^2.1.0:
 | 
			
		||||
  version "2.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4"
 | 
			
		||||
  integrity sha1-Aj1vObsVyXwHHp5g0A0TbqxfoLQ=
 | 
			
		||||
 | 
			
		||||
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6:
 | 
			
		||||
  version "16.12.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
 | 
			
		||||
| 
						 | 
				
			
			@ -7271,11 +7219,6 @@ schema-utils@^1.0.0:
 | 
			
		|||
    ajv-errors "^1.0.0"
 | 
			
		||||
    ajv-keywords "^3.1.0"
 | 
			
		||||
 | 
			
		||||
selection-is-backward@^1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/selection-is-backward/-/selection-is-backward-1.0.0.tgz#97a54633188a511aba6419fc5c1fa91b467e6be1"
 | 
			
		||||
  integrity sha1-l6VGMxiKURq6ZBn8XB+pG0Z+a+E=
 | 
			
		||||
 | 
			
		||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
 | 
			
		||||
  version "5.7.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
 | 
			
		||||
| 
						 | 
				
			
			@ -7371,93 +7314,6 @@ slash@^2.0.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
 | 
			
		||||
  integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
 | 
			
		||||
 | 
			
		||||
slate-base64-serializer@^0.2.69:
 | 
			
		||||
  version "0.2.112"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-base64-serializer/-/slate-base64-serializer-0.2.112.tgz#791d04a0ae7b9796844f068a904e185f2afc91f9"
 | 
			
		||||
  integrity sha512-Vo94bkCq8cbFj7Lutdh2RaM9S4WlLxnnMqZPKGUyefklUN4q2EzM/WUH7s9CIlLUH1qRfC/b0V25VJZr5XXTzA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    isomorphic-base64 "^1.0.2"
 | 
			
		||||
 | 
			
		||||
slate-dev-environment@^0.2.0, slate-dev-environment@^0.2.2:
 | 
			
		||||
  version "0.2.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-dev-environment/-/slate-dev-environment-0.2.2.tgz#bd8946e1fe4cf5447060c84a362a1d026ed8b77f"
 | 
			
		||||
  integrity sha512-JZ09llrRQu6JUsLJCUlGC0lB1r1qIAabAkSd454iyYBq6lDuY//Bypi3Jo8yzIfzZ4+mRLdQvl9e8MbeM9l48Q==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    is-in-browser "^1.1.3"
 | 
			
		||||
 | 
			
		||||
slate-dev-logger@^0.1.43:
 | 
			
		||||
  version "0.1.43"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-dev-logger/-/slate-dev-logger-0.1.43.tgz#77f6ca7207fcbf453a5516f3aa8b19794d1d26dc"
 | 
			
		||||
  integrity sha512-GkcPMGzmPVm85AL+jaKnzhIA0UH9ktQDEIDM+FuQtz+TAPcpPCQiRAaZ6I2p2uD0Hq9bImhKSCtHIa0qRxiVGw==
 | 
			
		||||
 | 
			
		||||
slate-dev-warning@^0.0.1:
 | 
			
		||||
  version "0.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-dev-warning/-/slate-dev-warning-0.0.1.tgz#f6c36731babea5e301b5bd504fe64911dd24200a"
 | 
			
		||||
  integrity sha512-QdXa+qmOG46VrTfnzn2gUVzs1WiO3Q+zCv3XomzMNGdgAJjCgHBs3jaeQD845h15loS3OJ181gCNAkB3dby6Hw==
 | 
			
		||||
 | 
			
		||||
slate-hotkeys@^0.2.5:
 | 
			
		||||
  version "0.2.9"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-hotkeys/-/slate-hotkeys-0.2.9.tgz#0cc9eb750a49ab9ef11601305b7c82b5402348e3"
 | 
			
		||||
  integrity sha512-y+C/s5vJEmBxo8fIqHmUcdViGwALL/A6Qow3sNG1OHYD5SI11tC2gfYtGbPh+2q0H7O4lufffCmFsP5bMaDHqA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    is-hotkey "0.1.4"
 | 
			
		||||
    slate-dev-environment "^0.2.2"
 | 
			
		||||
 | 
			
		||||
slate-html-serializer@^0.6.1:
 | 
			
		||||
  version "0.6.32"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-html-serializer/-/slate-html-serializer-0.6.32.tgz#69b0fcdb89a0bdcea28b60b6a90b944651ad3277"
 | 
			
		||||
  integrity sha512-x1RP1R2HMaVFflk9UXiuepcbN4wMoJRv0VWtxFw8efGNFmJfNBWME4iXAy6GNFRV0rRPlG3xCuQv2wHZ/+JMYw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    slate-dev-logger "^0.1.43"
 | 
			
		||||
    type-of "^2.0.1"
 | 
			
		||||
 | 
			
		||||
"slate-md-serializer@github:matrix-org/slate-md-serializer#f7c4ad3":
 | 
			
		||||
  version "3.1.0"
 | 
			
		||||
  resolved "https://codeload.github.com/matrix-org/slate-md-serializer/tar.gz/f7c4ad394f5af34d4c623de7909ce95ab78072d3"
 | 
			
		||||
 | 
			
		||||
slate-plain-serializer@^0.6.8:
 | 
			
		||||
  version "0.6.39"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-plain-serializer/-/slate-plain-serializer-0.6.39.tgz#5fb8d4dc530a2e7e0689548d48964ce242c4516a"
 | 
			
		||||
  integrity sha512-EGl+Y+9Fw9IULtPg8sttydaeiAoaibJolMXNfqI79+5GWTQwJFIbg24keKvsTw+3f2RieaPu8fcrKyujKtZ7ZQ==
 | 
			
		||||
 | 
			
		||||
slate-prop-types@^0.4.67:
 | 
			
		||||
  version "0.4.67"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-prop-types/-/slate-prop-types-0.4.67.tgz#c6aa74195466546a44fcb85d1c7b15fefe36ce6b"
 | 
			
		||||
  integrity sha512-FmdwitAw1Y69JHm326dfwP6Zd6R99jz1Im8jvKcnG2hytk72I1vIv6ct2CkNGwc3sg90+OIO/Rf18frYxxoTzw==
 | 
			
		||||
 | 
			
		||||
slate-react@^0.18.10:
 | 
			
		||||
  version "0.18.11"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate-react/-/slate-react-0.18.11.tgz#f452e7eb73f0271422d2a17e8090dcd8d889aef6"
 | 
			
		||||
  integrity sha512-7u0+LLabGaxjWYb0oTqUDcs3iCvJdaZwcGW6hLc1hFv06KkwaIxAqYpP8dUBRVlQd+0/X0TdyagCmf0IjFSPhg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    debug "^3.1.0"
 | 
			
		||||
    get-window "^1.1.1"
 | 
			
		||||
    is-window "^1.0.2"
 | 
			
		||||
    lodash "^4.1.1"
 | 
			
		||||
    memoize-one "^4.0.0"
 | 
			
		||||
    prop-types "^15.5.8"
 | 
			
		||||
    react-immutable-proptypes "^2.1.0"
 | 
			
		||||
    selection-is-backward "^1.0.0"
 | 
			
		||||
    slate-base64-serializer "^0.2.69"
 | 
			
		||||
    slate-dev-environment "^0.2.0"
 | 
			
		||||
    slate-dev-warning "^0.0.1"
 | 
			
		||||
    slate-hotkeys "^0.2.5"
 | 
			
		||||
    slate-plain-serializer "^0.6.8"
 | 
			
		||||
    slate-prop-types "^0.4.67"
 | 
			
		||||
 | 
			
		||||
slate@^0.41.2:
 | 
			
		||||
  version "0.41.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slate/-/slate-0.41.3.tgz#fa468de5db53afc453a0a7d7875b4de05737a900"
 | 
			
		||||
  integrity sha512-I/ymHWRxtoSOWYKh/SFgW3Vkkojt5ywWf7Wh4oBvaKic/3mAsM1wymyZmhnvSKK59IeE0JJzD4uyyQaM1KEFHA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    debug "^3.1.0"
 | 
			
		||||
    direction "^0.1.5"
 | 
			
		||||
    esrever "^0.2.0"
 | 
			
		||||
    is-plain-object "^2.0.4"
 | 
			
		||||
    lodash "^4.17.4"
 | 
			
		||||
    slate-dev-warning "^0.0.1"
 | 
			
		||||
    type-of "^2.0.1"
 | 
			
		||||
 | 
			
		||||
slice-ansi@^2.1.0:
 | 
			
		||||
  version "2.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
 | 
			
		||||
| 
						 | 
				
			
			@ -8264,11 +8120,6 @@ type-is@~1.6.17:
 | 
			
		|||
    media-typer "0.3.0"
 | 
			
		||||
    mime-types "~2.1.24"
 | 
			
		||||
 | 
			
		||||
type-of@^2.0.1:
 | 
			
		||||
  version "2.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/type-of/-/type-of-2.0.1.tgz#e72a1741896568e9f628378d816d6912f7f23972"
 | 
			
		||||
  integrity sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI=
 | 
			
		||||
 | 
			
		||||
typedarray@^0.0.6:
 | 
			
		||||
  version "0.0.6"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue