diff --git a/CHANGES.md b/CHANGES.md index 8f3a739182..4cd26ff896 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,6 +19,7 @@ - #3885: Don't show contact approval alerts when `allow_contact_requests` is false (e.g. anonymous mode) - #3886: Don't show OMEMO padlock icon when libsignal is not available (e.g. anonymous mode) - #3889: MUC join: Use room jids localpart as name in case name or identity not found +- #3710: Fix autocomplete adding a trailing space when selecting a group name with the mouse - #3916: Add support for XEP-0461 Message Replies, allowing users to reply to specific messages - #3939: Don't show invitations to groupchats in which the user is already present - #3941: add adhoc completed command result and text-multi as merged lines of text diff --git a/src/plugins/rosterview/modals/templates/accept-contact-request.js b/src/plugins/rosterview/modals/templates/accept-contact-request.js index ae0cb3f097..5b3d4d6420 100644 --- a/src/plugins/rosterview/modals/templates/accept-contact-request.js +++ b/src/plugins/rosterview/modals/templates/accept-contact-request.js @@ -28,7 +28,7 @@ export default (el) => {
${i18n_groups_help}
- +
diff --git a/src/plugins/rosterview/modals/templates/add-contact.js b/src/plugins/rosterview/modals/templates/add-contact.js index d87fcd47bf..96bec64b5c 100644 --- a/src/plugins/rosterview/modals/templates/add-contact.js +++ b/src/plugins/rosterview/modals/templates/add-contact.js @@ -69,7 +69,7 @@ export default (el) => {
${i18n_groups_help}
- +
${el.contact diff --git a/src/shared/autocomplete/autocomplete.js b/src/shared/autocomplete/autocomplete.js index 28de20c3dc..e07f433619 100644 --- a/src/shared/autocomplete/autocomplete.js +++ b/src/shared/autocomplete/autocomplete.js @@ -22,6 +22,7 @@ export class AutoComplete extends EventEmitter(Object) { this.suggestions = []; this.is_opened = false; this.match_current_word = false; // Match only the current word, otherwise all input is matched + this.suffix = ' '; // String to append after the autocompleted value this.sort = config.sort === false ? null : SORT_BY_QUERY_POSITION; this.filter = FILTER_CONTAINS; this.ac_triggers = []; // Array of keys (`ev.key`) values that will trigger auto-complete @@ -146,7 +147,7 @@ export class AutoComplete extends EventEmitter(Object) { */ insertValue(suggestion) { if (this.match_current_word) { - u.replaceCurrentWord(this.input, suggestion.value); + u.replaceCurrentWord(this.input, suggestion.value, this.suffix); } else { this.input.value = suggestion.value; } diff --git a/src/shared/autocomplete/component.js b/src/shared/autocomplete/component.js index 828cbe72ee..271f971152 100644 --- a/src/shared/autocomplete/component.js +++ b/src/shared/autocomplete/component.js @@ -66,6 +66,7 @@ export default class AutoCompleteComponent extends CustomElement { position: { type: String }, renderItem: { type: Function }, required: { type: Boolean }, + suffix: { type: String }, triggers: { type: String }, validate: { type: Function }, value: { type: String }, @@ -91,6 +92,7 @@ export default class AutoCompleteComponent extends CustomElement { this.renderItem = getAutoCompleteItem; this.required = false; + this.suffix = " "; this.triggers = ""; this.validate = null; this.value = ""; @@ -143,6 +145,7 @@ export default class AutoCompleteComponent extends CustomElement { list: this.list ?? (/** @param {string} q */(q) => this.getAutoCompleteList(q)), data: this.data, match_current_word: true, + suffix: this.suffix, max_items: this.max_items, min_chars: this.min_chars, item: this.renderItem, diff --git a/src/types/shared/autocomplete/autocomplete.d.ts b/src/types/shared/autocomplete/autocomplete.d.ts index 2ae3fde3bc..a1c6e2b27b 100644 --- a/src/types/shared/autocomplete/autocomplete.d.ts +++ b/src/types/shared/autocomplete/autocomplete.d.ts @@ -21,6 +21,7 @@ export class AutoComplete extends AutoComplete_base { suggestions: any[]; is_opened: boolean; match_current_word: boolean; + suffix: string; sort: (a: any, b: any) => number; filter: typeof FILTER_CONTAINS; ac_triggers: any[]; diff --git a/src/types/shared/autocomplete/component.d.ts b/src/types/shared/autocomplete/component.d.ts index 94570553d0..ca76b9b7a2 100644 --- a/src/types/shared/autocomplete/component.d.ts +++ b/src/types/shared/autocomplete/component.d.ts @@ -89,6 +89,9 @@ export default class AutoCompleteComponent extends CustomElement { required: { type: BooleanConstructor; }; + suffix: { + type: StringConstructor; + }; triggers: { type: StringConstructor; }; @@ -115,6 +118,7 @@ export default class AutoCompleteComponent extends CustomElement { position: string; renderItem: typeof getAutoCompleteItem; required: boolean; + suffix: string; triggers: string; validate: any; value: string; diff --git a/src/types/utils/form.d.ts b/src/types/utils/form.d.ts index 0e5ce70f37..a477c94111 100644 --- a/src/types/utils/form.d.ts +++ b/src/types/utils/form.d.ts @@ -30,8 +30,9 @@ export function getCurrentWord(input: HTMLInputElement | HTMLTextAreaElement, in /** * @param {HTMLInputElement} input - The HTMLElement in which text is being entered * @param {string} new_value + * @param {string} [suffix=' '] - String to append after the value */ -export function replaceCurrentWord(input: HTMLInputElement, new_value: string): void; +export function replaceCurrentWord(input: HTMLInputElement, new_value: string, suffix?: string): void; /** * Validates a JID for user input scenarios where locked_domain or default_domain * may be configured. When these settings are present, users can enter just a username diff --git a/src/types/utils/index.d.ts b/src/types/utils/index.d.ts index 013da0dfd8..19e851015c 100644 --- a/src/types/utils/index.d.ts +++ b/src/types/utils/index.d.ts @@ -201,7 +201,7 @@ declare const _default: { placeCaretAtEnd(textarea: HTMLTextAreaElement): void; isMentionBoundary(s: string): boolean; getCurrentWord(input: HTMLInputElement | HTMLTextAreaElement, index?: number, delineator?: string | RegExp): string; - replaceCurrentWord(input: HTMLInputElement, new_value: string): void; + replaceCurrentWord(input: HTMLInputElement, new_value: string, suffix?: string): void; isValidJIDInput(jid: string): boolean; isImageWithAlphaChannel(image_file: File): Promise; compressImage(file: File, options?: import("./types.js").CompressionOptions): Promise; diff --git a/src/utils/form.js b/src/utils/form.js index f8e7108578..d3d304e5f4 100644 --- a/src/utils/form.js +++ b/src/utils/form.js @@ -97,14 +97,15 @@ export function getCurrentWord(input, index, delineator) { /** * @param {HTMLInputElement} input - The HTMLElement in which text is being entered * @param {string} new_value + * @param {string} [suffix=' '] - String to append after the value */ -export function replaceCurrentWord(input, new_value) { +export function replaceCurrentWord(input, new_value, suffix = ' ') { const caret = input.selectionEnd || undefined; const current_word = input.value.slice(0, caret).split(/\s/).pop(); const value = input.value; const mention_boundary = isMentionBoundary(current_word[0]) ? current_word[0] : ''; - input.value = value.slice(0, caret - current_word.length) + mention_boundary + `${new_value} ` + value.slice(caret); - const selection_end = caret - current_word.length + new_value.length + 1; + input.value = value.slice(0, caret - current_word.length) + mention_boundary + `${new_value}${suffix}` + value.slice(caret); + const selection_end = caret - current_word.length + new_value.length + suffix.length; input.selectionEnd = mention_boundary ? selection_end + 1 : selection_end; }