Merge pull request #5510 from SimonBrandner/feature-surround-with
Add surround with featurepull/21833/head
						commit
						58575c335d
					
				| 
						 | 
				
			
			@ -55,6 +55,14 @@ const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.sourc
 | 
			
		|||
 | 
			
		||||
const IS_MAC = navigator.platform.indexOf("Mac") !== -1;
 | 
			
		||||
 | 
			
		||||
const SURROUND_WITH_CHARACTERS = ["\"", "_", "`", "'", "*", "~", "$"];
 | 
			
		||||
const SURROUND_WITH_DOUBLE_CHARACTERS = new Map([
 | 
			
		||||
    ["(", ")"],
 | 
			
		||||
    ["[", "]"],
 | 
			
		||||
    ["{", "}"],
 | 
			
		||||
    ["<", ">"],
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
function ctrlShortcutLabel(key: string): string {
 | 
			
		||||
    return (IS_MAC ? "⌘" : "Ctrl") + "+" + key;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +107,7 @@ interface IState {
 | 
			
		|||
    showVisualBell?: boolean;
 | 
			
		||||
    autoComplete?: AutocompleteWrapperModel;
 | 
			
		||||
    completionIndex?: number;
 | 
			
		||||
    surroundWith: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@replaceableComponent("views.rooms.BasicMessageEditor")
 | 
			
		||||
| 
						 | 
				
			
			@ -117,12 +126,14 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
 | 
			
		|||
 | 
			
		||||
    private readonly emoticonSettingHandle: string;
 | 
			
		||||
    private readonly shouldShowPillAvatarSettingHandle: string;
 | 
			
		||||
    private readonly surroundWithHandle: string;
 | 
			
		||||
    private readonly historyManager = new HistoryManager();
 | 
			
		||||
 | 
			
		||||
    constructor(props) {
 | 
			
		||||
        super(props);
 | 
			
		||||
        this.state = {
 | 
			
		||||
            showPillAvatar: SettingsStore.getValue("Pill.shouldShowPillAvatar"),
 | 
			
		||||
            surroundWith: SettingsStore.getValue("MessageComposerInput.surroundWith"),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.emoticonSettingHandle = SettingsStore.watchSetting('MessageComposerInput.autoReplaceEmoji', null,
 | 
			
		||||
| 
						 | 
				
			
			@ -130,6 +141,8 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
 | 
			
		|||
        this.configureEmoticonAutoReplace();
 | 
			
		||||
        this.shouldShowPillAvatarSettingHandle = SettingsStore.watchSetting("Pill.shouldShowPillAvatar", null,
 | 
			
		||||
            this.configureShouldShowPillAvatar);
 | 
			
		||||
        this.surroundWithHandle = SettingsStore.watchSetting("MessageComposerInput.surroundWith", null,
 | 
			
		||||
            this.surroundWithSettingChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate(prevProps: IProps) {
 | 
			
		||||
| 
						 | 
				
			
			@ -422,6 +435,28 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
 | 
			
		|||
    private onKeyDown = (event: React.KeyboardEvent): void => {
 | 
			
		||||
        const model = this.props.model;
 | 
			
		||||
        let handled = false;
 | 
			
		||||
 | 
			
		||||
        if (this.state.surroundWith && document.getSelection().type != "Caret") {
 | 
			
		||||
            // This surrounds the selected text with a character. This is
 | 
			
		||||
            // intentionally left out of the keybinding manager as the keybinds
 | 
			
		||||
            // here shouldn't be changeable
 | 
			
		||||
 | 
			
		||||
            const selectionRange = getRangeForSelection(
 | 
			
		||||
                this.editorRef.current,
 | 
			
		||||
                this.props.model,
 | 
			
		||||
                document.getSelection(),
 | 
			
		||||
            );
 | 
			
		||||
            // trim the range as we want it to exclude leading/trailing spaces
 | 
			
		||||
            selectionRange.trim();
 | 
			
		||||
 | 
			
		||||
            if ([...SURROUND_WITH_DOUBLE_CHARACTERS.keys(), ...SURROUND_WITH_CHARACTERS].includes(event.key)) {
 | 
			
		||||
                this.historyManager.ensureLastChangesPushed(this.props.model);
 | 
			
		||||
                this.modifiedFlag = true;
 | 
			
		||||
                toggleInlineFormat(selectionRange, event.key, SURROUND_WITH_DOUBLE_CHARACTERS.get(event.key));
 | 
			
		||||
                handled = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const action = getKeyBindingsManager().getMessageComposerAction(event);
 | 
			
		||||
        switch (action) {
 | 
			
		||||
            case MessageComposerAction.FormatBold:
 | 
			
		||||
| 
						 | 
				
			
			@ -574,6 +609,11 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
 | 
			
		|||
        this.setState({ showPillAvatar });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private surroundWithSettingChanged = () => {
 | 
			
		||||
        const surroundWith = SettingsStore.getValue("MessageComposerInput.surroundWith");
 | 
			
		||||
        this.setState({ surroundWith });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    componentWillUnmount() {
 | 
			
		||||
        document.removeEventListener("selectionchange", this.onSelectionChange);
 | 
			
		||||
        this.editorRef.current.removeEventListener("input", this.onInput, true);
 | 
			
		||||
| 
						 | 
				
			
			@ -581,6 +621,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
 | 
			
		|||
        this.editorRef.current.removeEventListener("compositionend", this.onCompositionEnd, true);
 | 
			
		||||
        SettingsStore.unwatchSetting(this.emoticonSettingHandle);
 | 
			
		||||
        SettingsStore.unwatchSetting(this.shouldShowPillAvatarSettingHandle);
 | 
			
		||||
        SettingsStore.unwatchSetting(this.surroundWithHandle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    componentDidMount() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,7 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
 | 
			
		|||
        'MessageComposerInput.suggestEmoji',
 | 
			
		||||
        'sendTypingNotifications',
 | 
			
		||||
        'MessageComposerInput.ctrlEnterToSend',
 | 
			
		||||
        'MessageComposerInput.surroundWith',
 | 
			
		||||
        'MessageComposerInput.showStickersButton',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -847,6 +847,7 @@
 | 
			
		|||
    "Use Ctrl + F to search timeline": "Use Ctrl + F to search timeline",
 | 
			
		||||
    "Use Command + Enter to send a message": "Use Command + Enter to send a message",
 | 
			
		||||
    "Use Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message",
 | 
			
		||||
    "Surround selected text when typing special characters": "Surround selected text when typing special characters",
 | 
			
		||||
    "Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
 | 
			
		||||
    "Mirror local video feed": "Mirror local video feed",
 | 
			
		||||
    "Enable Community Filter Panel": "Enable Community Filter Panel",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -449,6 +449,11 @@ export const SETTINGS: {[setting: string]: ISetting} = {
 | 
			
		|||
        displayName: isMac ? _td("Use Command + Enter to send a message") : _td("Use Ctrl + Enter to send a message"),
 | 
			
		||||
        default: false,
 | 
			
		||||
    },
 | 
			
		||||
    "MessageComposerInput.surroundWith": {
 | 
			
		||||
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
 | 
			
		||||
        displayName: _td("Surround selected text when typing special characters"),
 | 
			
		||||
        default: false,
 | 
			
		||||
    },
 | 
			
		||||
    "MessageComposerInput.autoReplaceEmoji": {
 | 
			
		||||
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
 | 
			
		||||
        displayName: _td('Automatically replace plain text Emoji'),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue