diff --git a/playwright/e2e/permalinks/permalinks.spec.ts b/playwright/e2e/permalinks/permalinks.spec.ts
index e7657b1394..3f6fddbd0d 100644
--- a/playwright/e2e/permalinks/permalinks.spec.ts
+++ b/playwright/e2e/permalinks/permalinks.spec.ts
@@ -100,3 +100,51 @@ test.describe("permalinks", () => {
});
});
});
+
+test.describe("triple-click message selection", () => {
+ test.use({
+ displayName: "Alice",
+ });
+
+ test("should select entire message line when triple-clicking on message with pills", async ({
+ page,
+ app,
+ user,
+ bot,
+ }) => {
+ await bot.prepareClient();
+
+ const roomId = await app.client.createRoom({ name: "Test Room" });
+ await app.client.inviteUser(roomId, bot.credentials.userId);
+ await app.viewRoomByName("Test Room");
+
+ // Send a message with user and room pills
+ await app.client.sendMessage(
+ roomId,
+ `Testing triple-click message selection. ` +
+ `User: ${permalinkPrefix}${bot.credentials.userId}, ` +
+ `Room: ${permalinkPrefix}${roomId}, ` +
+ `Message: ${permalinkPrefix}${roomId}/$dummy-event, ` +
+ `and @room mention.`,
+ );
+
+ const timeline = page.locator(".mx_RoomView_timeline");
+ const messageTile = timeline.locator(".mx_EventTile").last();
+
+ // Triple-click on the message body to select its entire content
+ const messageBody = messageTile.locator(".mx_EventTile_body");
+ await messageBody.click({ clickCount: 3 });
+
+ // Get the expected text content of the message, including pills
+ const expectedText = await messageBody.innerText();
+
+ // Get the currently selected text from the page
+ const selectedText = await page.evaluate(() => {
+ const selection = window.getSelection();
+ return selection ? selection.toString().trim() : "";
+ });
+
+ // Verify that the selected text exactly matches the message content
+ expect(selectedText).toBe(expectedText);
+ });
+});
diff --git a/res/css/views/elements/_Pill.pcss b/res/css/views/elements/_Pill.pcss
index d692f812a4..4b3fd3bb68 100644
--- a/res/css/views/elements/_Pill.pcss
+++ b/res/css/views/elements/_Pill.pcss
@@ -11,8 +11,7 @@ Please see LICENSE files in the repository root for full details.
line-height: $font-17px;
border-radius: $font-16px;
vertical-align: text-top;
- display: inline-flex;
- align-items: center;
+ display: inline-block;
box-sizing: border-box;
max-width: 100%;
overflow: hidden;
@@ -57,6 +56,8 @@ Please see LICENSE files in the repository root for full details.
margin-inline-start: -0.3em; /* Otherwise the gap is too large */
margin-inline-end: 0.2em;
min-width: $font-16px; /* ensure the avatar is not compressed */
+ user-select: text;
+ vertical-align: -2.5px;
}
.mx_Pill_text {
diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json
index 69b4127ea8..b276c2ded5 100644
--- a/src/i18n/strings/cs.json
+++ b/src/i18n/strings/cs.json
@@ -646,12 +646,12 @@
"mode_plain": "Skrýt formátování",
"mode_rich_text": "Zobrazit formátování",
"no_perms_notice": "Nemáte oprávnění zveřejňovat příspěvky v této místnosti",
- "placeholder": "Odeslat zprávu…",
- "placeholder_encrypted": "Odeslat šifrovanou zprávu…",
- "placeholder_reply": "Odpovědět…",
- "placeholder_reply_encrypted": "Odeslat šifrovanou odpověď…",
- "placeholder_thread": "Odpovědět na vlákno…",
- "placeholder_thread_encrypted": "Odpovědět na zašifrované vlákno…",
+ "placeholder": "Odeslat nešifrovanou zprávu…",
+ "placeholder_encrypted": "Odeslat zprávu...",
+ "placeholder_reply": "Odeslat nešifrovanou odpověď…",
+ "placeholder_reply_encrypted": "Odeslat odpověď…",
+ "placeholder_thread": "Odpovědět v nešifrovaném vláknu…",
+ "placeholder_thread_encrypted": "Odpovědět ve vláknu…",
"poll_button": "Hlasování",
"poll_button_no_perms_description": "Nemáte oprávnění zahajovat hlasování v této místnosti.",
"poll_button_no_perms_title": "Vyžaduje oprávnění",
@@ -922,7 +922,8 @@
},
"privacy_warning": "Ujistěte se, že tuto obrazovku nikdo nevidí!",
"restoring": "Obnovení klíčů ze zálohy",
- "security_key_title": "Klíč pro obnovení"
+ "security_key_label": "Klíč pro obnovení",
+ "security_key_title": "Zadejte klíč pro obnovení"
},
"bootstrap_title": "Příprava klíčů",
"confirm_encryption_setup_body": "Kliknutím na tlačítko níže potvrďte nastavení šifrování.",
@@ -1367,6 +1368,10 @@
"name_email_mxid_share_space": "Pozvěte někoho pomocí jeho jména, e-mailové adresy, uživatelského jména (například ) nebo sdílejte tento prostor.",
"name_mxid_share_room": "Pozvěte někoho pomocí svého jména, uživatelského jména (například ) nebo sdílejte tuto místnost.",
"name_mxid_share_space": "Pozvěte někoho pomocí jeho jména, uživatelského jména (například ) nebo sdílejte tento prostor.",
+ "progress": {
+ "dont_close": "Nezavírejte aplikaci, dokud neskončíte.",
+ "preparing": "Příprava pozvánek..."
+ },
"recents_section": "Nedávné konverzace",
"room_failed_partial": "Poslali jsme ostatním, ale níže uvedení lidé nemohli být pozváni do ",
"room_failed_partial_title": "Některé pozvánky nebylo možné odeslat",
@@ -1535,6 +1540,9 @@
"render_reaction_images_description": "Někdy se označují jako \"vlastní emoji\".",
"report_to_moderators": "Nahlásit moderátorům",
"report_to_moderators_description": "V místnostech, které podporují moderování, můžete pomocí tlačítka \"Nahlásit\" nahlásit zneužití moderátorům místnosti.",
+ "share_history_on_invite": "Sdílet šifrovanou historii s novými členy",
+ "share_history_on_invite_description": "Při pozvání uživatele do šifrované místnosti, u které je viditelnost historie nastavena na „sdílená“, sdílet šifrovanou historii s tímto uživatelem a přijmout šifrovanou historii, když jste pozváni do takové místnosti.",
+ "share_history_on_invite_warning": "Tato funkce je EXPERIMENTÁLNÍ a nejsou v ní implementována všechna bezpečnostní opatření. Neaktivujte ji na produkčních účtech.",
"sliding_sync": "Režim klouzavé synchronizace",
"sliding_sync_description": "V aktivním vývoji, nelze zakázat.",
"sliding_sync_disabled_notice": "Pro vypnutí se odhlaste a znovu přihlaste",
@@ -1657,6 +1665,7 @@
"filter_placeholder": "Najít člena místnosti",
"invite_button_no_perms_tooltip": "Nemáte oprávnění zvát uživatele",
"invited_label": "Pozván",
+ "list_title": "Seznam členů",
"no_matches": "Žádné shody"
},
"member_list_back_action_label": "Členové místnosti",
@@ -1761,6 +1770,7 @@
},
"power_level": {
"admin": "Správce",
+ "creator": "Vlastník",
"custom": "Vlastní (%(level)s)",
"custom_level": "Vlastní úroveň",
"default": "Výchozí",
@@ -1914,6 +1924,7 @@
"thread_list": {
"context_menu_label": "Možnosti vláken"
},
+ "title": "Pravý panel",
"video_room_chat": {
"title": "Chatovat"
}
@@ -3400,6 +3411,7 @@
"unable_to_find": "Pokusili jste se načíst bod na časové ose místnosti, ale nepodařilo se ho najít."
},
"m.audio": {
+ "audio_player": "Audio přehrávač",
"error_downloading_audio": "Chyba při stahování audia",
"error_processing_audio": "Došlo k chybě při zpracovávání hlasové zprávy",
"error_processing_voice_message": "Chyba při zpracování hlasové zprávy",
diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json
index bdaac9dc69..3a9119f107 100644
--- a/src/i18n/strings/fr.json
+++ b/src/i18n/strings/fr.json
@@ -654,6 +654,7 @@
"poll_button_no_perms_description": "Vous n’avez pas la permission de démarrer un sondage dans ce salon.",
"poll_button_no_perms_title": "Autorisation requise",
"replying_title": "Répond",
+ "room_unencrypted": "Les messages dans ce salon ne sont pas chiffrés de bout en bout",
"room_upgraded_link": "La discussion continue ici.",
"room_upgraded_notice": "Ce salon a été remplacé et n’est plus actif.",
"send_button_title": "Envoyer le message",
@@ -1366,6 +1367,10 @@
"name_email_mxid_share_space": "Invitez quelqu’un grâce à son nom, adresse e-mail, nom d’utilisateur (tel que ) ou partagez cet espace.",
"name_mxid_share_room": "Invitez quelqu’un à partir de son nom, pseudo (comme ) ou partagez ce salon.",
"name_mxid_share_space": "Invitez quelqu’un grâce à son nom, nom d’utilisateur (tel que ) ou partagez cet espace.",
+ "progress": {
+ "dont_close": "Ne fermez pas l\"application tant que l'opération est en cours",
+ "preparing": "Préparation des invitations..."
+ },
"recents_section": "Conversations récentes",
"room_failed_partial": "Nous avons envoyé les invitations, mais les personnes ci-dessous n’ont pas pu être invitées à rejoindre ",
"room_failed_partial_title": "Certaines invitations n’ont pas pu être envoyées",
diff --git a/src/i18n/strings/nb_NO.json b/src/i18n/strings/nb_NO.json
index 89843712dd..1e9b0f0220 100644
--- a/src/i18n/strings/nb_NO.json
+++ b/src/i18n/strings/nb_NO.json
@@ -654,6 +654,7 @@
"poll_button_no_perms_description": "Du har ikke tillatelse til å starte avstemninger i dette rommet.",
"poll_button_no_perms_title": "Tillatelse kreves",
"replying_title": "Svarer på",
+ "room_unencrypted": "Meldinger i dette rommet er ikke ende-til-ende krypterte",
"room_upgraded_link": "Samtalen fortsetter her.",
"room_upgraded_notice": "Dette rommet har blitt erstattet og er ikke lenger aktivt.",
"send_button_title": "Send melding",
diff --git a/test/unit-tests/__snapshots__/Terms-test.tsx.snap b/test/unit-tests/__snapshots__/Terms-test.tsx.snap
index a35963cd51..7bd269d3ea 100644
--- a/test/unit-tests/__snapshots__/Terms-test.tsx.snap
+++ b/test/unit-tests/__snapshots__/Terms-test.tsx.snap
@@ -7,6 +7,7 @@ exports[`dialogTermsInteractionCallback should render a dialog with the expected
class=""
data-focus-lock-disabled="false"
role="dialog"
+ tabindex="-1"
>
when key backup is disabled 1`] = `
class="mx_KeyBackupFailedDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
+ tabindex="-1"
>
when key backup is enabled 1`] = `
class="mx_KeyBackupFailedDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
+ tabindex="-1"
>
with an existing session onAction() room actions leave_r
class="mx_QuestionDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
+ tabindex="-1"
>
with an existing session onAction() room actions leave_r
class="mx_QuestionDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
+ tabindex="-1"
>