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) => {
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