Merge pull request #5510 from SimonBrandner/feature-surround-with
Add surround with feature
This commit is contained in:
commit
58575c335d
4 changed files with 48 additions and 0 deletions
|
@ -55,6 +55,14 @@ const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.sourc
|
||||||
|
|
||||||
const IS_MAC = navigator.platform.indexOf("Mac") !== -1;
|
const IS_MAC = navigator.platform.indexOf("Mac") !== -1;
|
||||||
|
|
||||||
|
const SURROUND_WITH_CHARACTERS = ["\"", "_", "`", "'", "*", "~", "$"];
|
||||||
|
const SURROUND_WITH_DOUBLE_CHARACTERS = new Map([
|
||||||
|
["(", ")"],
|
||||||
|
["[", "]"],
|
||||||
|
["{", "}"],
|
||||||
|
["<", ">"],
|
||||||
|
]);
|
||||||
|
|
||||||
function ctrlShortcutLabel(key: string): string {
|
function ctrlShortcutLabel(key: string): string {
|
||||||
return (IS_MAC ? "⌘" : "Ctrl") + "+" + key;
|
return (IS_MAC ? "⌘" : "Ctrl") + "+" + key;
|
||||||
}
|
}
|
||||||
|
@ -99,6 +107,7 @@ interface IState {
|
||||||
showVisualBell?: boolean;
|
showVisualBell?: boolean;
|
||||||
autoComplete?: AutocompleteWrapperModel;
|
autoComplete?: AutocompleteWrapperModel;
|
||||||
completionIndex?: number;
|
completionIndex?: number;
|
||||||
|
surroundWith: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.BasicMessageEditor")
|
@replaceableComponent("views.rooms.BasicMessageEditor")
|
||||||
|
@ -117,12 +126,14 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
|
|
||||||
private readonly emoticonSettingHandle: string;
|
private readonly emoticonSettingHandle: string;
|
||||||
private readonly shouldShowPillAvatarSettingHandle: string;
|
private readonly shouldShowPillAvatarSettingHandle: string;
|
||||||
|
private readonly surroundWithHandle: string;
|
||||||
private readonly historyManager = new HistoryManager();
|
private readonly historyManager = new HistoryManager();
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
showPillAvatar: SettingsStore.getValue("Pill.shouldShowPillAvatar"),
|
showPillAvatar: SettingsStore.getValue("Pill.shouldShowPillAvatar"),
|
||||||
|
surroundWith: SettingsStore.getValue("MessageComposerInput.surroundWith"),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.emoticonSettingHandle = SettingsStore.watchSetting('MessageComposerInput.autoReplaceEmoji', null,
|
this.emoticonSettingHandle = SettingsStore.watchSetting('MessageComposerInput.autoReplaceEmoji', null,
|
||||||
|
@ -130,6 +141,8 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
this.configureEmoticonAutoReplace();
|
this.configureEmoticonAutoReplace();
|
||||||
this.shouldShowPillAvatarSettingHandle = SettingsStore.watchSetting("Pill.shouldShowPillAvatar", null,
|
this.shouldShowPillAvatarSettingHandle = SettingsStore.watchSetting("Pill.shouldShowPillAvatar", null,
|
||||||
this.configureShouldShowPillAvatar);
|
this.configureShouldShowPillAvatar);
|
||||||
|
this.surroundWithHandle = SettingsStore.watchSetting("MessageComposerInput.surroundWith", null,
|
||||||
|
this.surroundWithSettingChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: IProps) {
|
public componentDidUpdate(prevProps: IProps) {
|
||||||
|
@ -422,6 +435,28 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
private onKeyDown = (event: React.KeyboardEvent): void => {
|
private onKeyDown = (event: React.KeyboardEvent): void => {
|
||||||
const model = this.props.model;
|
const model = this.props.model;
|
||||||
let handled = false;
|
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);
|
const action = getKeyBindingsManager().getMessageComposerAction(event);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MessageComposerAction.FormatBold:
|
case MessageComposerAction.FormatBold:
|
||||||
|
@ -574,6 +609,11 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
this.setState({ showPillAvatar });
|
this.setState({ showPillAvatar });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private surroundWithSettingChanged = () => {
|
||||||
|
const surroundWith = SettingsStore.getValue("MessageComposerInput.surroundWith");
|
||||||
|
this.setState({ surroundWith });
|
||||||
|
};
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.removeEventListener("selectionchange", this.onSelectionChange);
|
document.removeEventListener("selectionchange", this.onSelectionChange);
|
||||||
this.editorRef.current.removeEventListener("input", this.onInput, true);
|
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);
|
this.editorRef.current.removeEventListener("compositionend", this.onCompositionEnd, true);
|
||||||
SettingsStore.unwatchSetting(this.emoticonSettingHandle);
|
SettingsStore.unwatchSetting(this.emoticonSettingHandle);
|
||||||
SettingsStore.unwatchSetting(this.shouldShowPillAvatarSettingHandle);
|
SettingsStore.unwatchSetting(this.shouldShowPillAvatarSettingHandle);
|
||||||
|
SettingsStore.unwatchSetting(this.surroundWithHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
|
@ -61,6 +61,7 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
|
||||||
'MessageComposerInput.suggestEmoji',
|
'MessageComposerInput.suggestEmoji',
|
||||||
'sendTypingNotifications',
|
'sendTypingNotifications',
|
||||||
'MessageComposerInput.ctrlEnterToSend',
|
'MessageComposerInput.ctrlEnterToSend',
|
||||||
|
'MessageComposerInput.surroundWith',
|
||||||
'MessageComposerInput.showStickersButton',
|
'MessageComposerInput.showStickersButton',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -847,6 +847,7 @@
|
||||||
"Use Ctrl + F to search timeline": "Use Ctrl + F to search timeline",
|
"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 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",
|
"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",
|
"Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
|
||||||
"Mirror local video feed": "Mirror local video feed",
|
"Mirror local video feed": "Mirror local video feed",
|
||||||
"Enable Community Filter Panel": "Enable Community Filter Panel",
|
"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"),
|
displayName: isMac ? _td("Use Command + Enter to send a message") : _td("Use Ctrl + Enter to send a message"),
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
"MessageComposerInput.surroundWith": {
|
||||||
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
|
displayName: _td("Surround selected text when typing special characters"),
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
"MessageComposerInput.autoReplaceEmoji": {
|
"MessageComposerInput.autoReplaceEmoji": {
|
||||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
displayName: _td('Automatically replace plain text Emoji'),
|
displayName: _td('Automatically replace plain text Emoji'),
|
||||||
|
|
Loading…
Reference in a new issue