From 71b5f60b7e255dac65e461d5282a21b223a46a5d Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 14:57:40 +0100
Subject: [PATCH 01/11] feat: add icons to default MessageActions
---
src/components/Message/hooks/useUserRole.ts | 6 +-
.../MessageActions/RemindMeSubmenu.tsx | 9 +-
src/components/MessageActions/defaults.tsx | 248 ++++++++++++++----
.../hooks/useBaseMessageActionSetFilter.ts | 18 +-
.../styling/MessageActions.scss | 7 +
src/i18n/de.json | 20 ++
src/i18n/en.json | 20 ++
src/i18n/es.json | 20 ++
src/i18n/fr.json | 20 ++
src/i18n/hi.json | 20 ++
src/i18n/it.json | 20 ++
src/i18n/ja.json | 20 ++
src/i18n/ko.json | 20 ++
src/i18n/nl.json | 20 ++
src/i18n/pt.json | 20 ++
src/i18n/ru.json | 20 ++
src/i18n/tr.json | 20 ++
17 files changed, 473 insertions(+), 55 deletions(-)
diff --git a/src/components/Message/hooks/useUserRole.ts b/src/components/Message/hooks/useUserRole.ts
index bcbcf7a50b..443eee9f2d 100644
--- a/src/components/Message/hooks/useUserRole.ts
+++ b/src/components/Message/hooks/useUserRole.ts
@@ -46,13 +46,16 @@ export const useUserRole = (
(isMyMessage && channelCapabilities['delete-own-message']);
const canFlag = !isMyMessage && channelCapabilities['flag-message'];
- const canMarkUnread = channelCapabilities['read-events'];
const canMute = !isMyMessage && channelCapabilities['mute-channel'];
+ const canBlockUser = !isMyMessage;
+ const canMarkUnread = channelCapabilities['read-events'];
const canQuote = !disableQuotedMessages && channelCapabilities['quote-message'];
const canReact = channelCapabilities['send-reaction'];
const canReply = channelCapabilities['send-reply'];
+ const canSendMessage = channelCapabilities['send-message'];
return {
+ canBlockUser,
canDelete,
canEdit,
canFlag,
@@ -61,6 +64,7 @@ export const useUserRole = (
canQuote,
canReact,
canReply,
+ canSendMessage,
isAdmin,
isModerator,
isMyMessage,
diff --git a/src/components/MessageActions/RemindMeSubmenu.tsx b/src/components/MessageActions/RemindMeSubmenu.tsx
index 5713dae2a1..9c9d1899ae 100644
--- a/src/components/MessageActions/RemindMeSubmenu.tsx
+++ b/src/components/MessageActions/RemindMeSubmenu.tsx
@@ -7,7 +7,7 @@ import {
ContextMenuHeader,
useContextMenuContext,
} from '../Dialog';
-import { IconChevronLeft } from '../Icons';
+import { IconBellNotification, IconChevronLeft } from '../Icons';
// todo: do we need to have isMine as a prop?
export type RemindMeActionButtonProps = { isMine: boolean } & BaseContextMenuButtonProps;
@@ -20,7 +20,12 @@ export const RemindMeActionButton = ({
const { t } = useTranslationContext();
return (
-
+
{t('Remind Me')}
);
diff --git a/src/components/MessageActions/defaults.tsx b/src/components/MessageActions/defaults.tsx
index 2406572a06..239a53c6c8 100644
--- a/src/components/MessageActions/defaults.tsx
+++ b/src/components/MessageActions/defaults.tsx
@@ -1,7 +1,22 @@
/* eslint-disable sort-keys */
import React from 'react';
-import { isUserMuted, useMessageComposer, useMessageReminder } from '../../components';
+import {
+ IconArrowRotateClockwise,
+ IconBookmark,
+ IconBubbleText6ChatMessage,
+ IconBubbleWideNotificationChatMessage,
+ IconCircleBanSign,
+ IconEditBig,
+ IconFlag2,
+ IconMute,
+ IconPin,
+ IconSquareBehindSquare2_Copy,
+ IconTrashBin,
+ isUserMuted,
+ useMessageComposer,
+ useMessageReminder,
+} from '../../components';
import {
ReactionIcon as DefaultReactionIcon,
ThreadIcon,
@@ -17,12 +32,35 @@ import type { ContextMenuItemProps } from '../../components/Dialog';
import { ContextMenuButton } from '../../components/Dialog';
import type { MessageActionSetItem } from './MessageActions';
import { QuickMessageActionsButton } from './QuickMessageActionButton';
+import clsx from 'clsx';
const msgActionsBoxButtonClassName =
'str-chat__message-actions-list-item-button' as const;
+const msgActionsBoxButtonClassNameDestructive =
+ 'str-chat__message-actions-list-item-button--destructive' as const;
const DefaultMessageActionComponents = {
dropdown: {
+ ThreadReply({ closeMenu }: ContextMenuItemProps) {
+ const { handleOpenThread } = useMessageContext();
+ const { t } = useTranslationContext();
+
+ return (
+ {
+ handleOpenThread(e);
+ closeMenu();
+ }}
+ >
+ {t('Thread Reply')}
+
+ );
+ },
Quote({ closeMenu }: ContextMenuItemProps) {
const { message } = useMessageContext();
const { t } = useTranslationContext();
@@ -43,6 +81,7 @@ const DefaultMessageActionComponents = {
return (
{
@@ -57,66 +96,58 @@ const DefaultMessageActionComponents = {
Pin({ closeMenu }: ContextMenuItemProps) {
const { handlePin, message } = useMessageContext();
const { t } = useTranslationContext();
-
+ const isPinned = !!message.pinned;
return (
{
handlePin(event);
closeMenu();
}}
>
- {!message.pinned ? t('Pin') : t('Unpin')}
+ {isPinned ? t('Unpin') : t('Pin')}
);
},
- MarkUnread({ closeMenu }: ContextMenuItemProps) {
- const { handleMarkUnread } = useMessageContext();
- const { t } = useTranslationContext();
-
- return (
- {
- handleMarkUnread(event);
- closeMenu();
- }}
- >
- {t('Mark as unread')}
-
- );
- },
- Flag({ closeMenu }: ContextMenuItemProps) {
- const { handleFlag } = useMessageContext();
+ CopyMessageText({ closeMenu }: ContextMenuItemProps) {
+ const { message } = useMessageContext();
const { t } = useTranslationContext();
return (
{
- handleFlag(event);
+ Icon={IconSquareBehindSquare2_Copy}
+ onClick={() => {
+ if (message.text) navigator.clipboard.writeText(message.text);
closeMenu();
}}
>
- {t('Flag')}
+ {t('Copy Message')}
);
},
- Mute({ closeMenu }: ContextMenuItemProps) {
- const { handleMute, message } = useMessageContext();
- const { mutes } = useChatContext();
+ Resend({ closeMenu }: ContextMenuItemProps) {
+ const messageComposer = useMessageComposer();
+ const { message } = useMessageContext();
const { t } = useTranslationContext();
return (
{
- handleMute(event);
+ Icon={IconArrowRotateClockwise}
+ onClick={() => {
+ messageComposer.initState({ composition: message });
closeMenu();
}}
>
- {isUserMuted(message, mutes) ? t('Unmute') : t('Mute')}
+ {t('Resend')}
);
},
@@ -127,7 +158,10 @@ const DefaultMessageActionComponents = {
return (
{
messageComposer.initState({ composition: message });
closeMenu();
@@ -137,26 +171,33 @@ const DefaultMessageActionComponents = {
);
},
- Delete({ closeMenu }: ContextMenuItemProps) {
- const { handleDelete } = useMessageContext();
+ MarkUnread({ closeMenu }: ContextMenuItemProps) {
+ const { handleMarkUnread } = useMessageContext();
const { t } = useTranslationContext();
return (
{
- handleDelete(event);
+ handleMarkUnread(event);
closeMenu();
}}
>
- {t('Delete')}
+ {t('Mark as unread')}
);
},
RemindMe({ openSubmenu }: ContextMenuItemProps) {
const { isMyMessage } = useMessageContext();
+ const { t } = useTranslationContext();
+
return (
{
if (reminder) {
client.reminders.deleteReminder(reminder.id);
@@ -191,6 +235,95 @@ const DefaultMessageActionComponents = {
);
},
+ Flag({ closeMenu }: ContextMenuItemProps) {
+ const { handleFlag } = useMessageContext();
+ const { t } = useTranslationContext();
+
+ return (
+ {
+ handleFlag(event);
+ closeMenu();
+ }}
+ >
+ {t('Flag')}
+
+ );
+ },
+ Mute({ closeMenu }: ContextMenuItemProps) {
+ const { handleMute, message } = useMessageContext();
+ const { mutes } = useChatContext();
+ const { t } = useTranslationContext();
+
+ const isMuted = isUserMuted(message, mutes);
+ return (
+ {
+ handleMute(event);
+ closeMenu();
+ }}
+ >
+ {isMuted ? t('Unmute') : t('Mute')}
+
+ );
+ },
+ Delete({ closeMenu }: ContextMenuItemProps) {
+ const { handleDelete } = useMessageContext();
+ const { t } = useTranslationContext();
+
+ return (
+ {
+ handleDelete(event);
+ closeMenu();
+ }}
+ >
+ {t('Delete')}
+
+ );
+ },
+ BlockUser({ closeMenu }: ContextMenuItemProps) {
+ const { client } = useChatContext();
+ const { message } = useMessageContext();
+ const { t } = useTranslationContext();
+ const isBlocked = false; // !!client.blockedUsers[targetId]
+ return (
+ {
+ const targetId = message.user?.id;
+ if (targetId) {
+ if (isBlocked) client.unBlockUser(targetId);
+ else client.blockUser(targetId);
+ }
+ closeMenu();
+ }}
+ >
+ {isBlocked ? t('Unblock User') : t('Block User')}
+
+ );
+ },
},
quick: {
React() {
@@ -227,24 +360,14 @@ export const defaultMessageActionSet: MessageActionSetItem[] = [
type: 'react',
},
{
- Component: DefaultMessageActionComponents.dropdown.Delete,
- placement: 'dropdown',
- type: 'delete',
- },
- {
- Component: DefaultMessageActionComponents.dropdown.Edit,
- placement: 'dropdown',
- type: 'edit',
- },
- {
- Component: DefaultMessageActionComponents.dropdown.Mute,
+ Component: DefaultMessageActionComponents.dropdown.ThreadReply,
placement: 'dropdown',
- type: 'mute',
+ type: 'reply',
},
{
- Component: DefaultMessageActionComponents.dropdown.Flag,
+ Component: DefaultMessageActionComponents.dropdown.Quote,
placement: 'dropdown',
- type: 'flag',
+ type: 'quote',
},
{
Component: DefaultMessageActionComponents.dropdown.Pin,
@@ -252,15 +375,20 @@ export const defaultMessageActionSet: MessageActionSetItem[] = [
type: 'pin',
},
{
- Component: DefaultMessageActionComponents.dropdown.Quote,
+ Component: DefaultMessageActionComponents.dropdown.CopyMessageText,
placement: 'dropdown',
- type: 'quote',
+ type: 'copyMessageText',
},
{
Component: DefaultMessageActionComponents.dropdown.MarkUnread,
placement: 'dropdown',
type: 'markUnread',
},
+ {
+ Component: DefaultMessageActionComponents.dropdown.Edit,
+ placement: 'dropdown',
+ type: 'edit',
+ },
{
Component: DefaultMessageActionComponents.dropdown.RemindMe,
placement: 'dropdown',
@@ -271,4 +399,24 @@ export const defaultMessageActionSet: MessageActionSetItem[] = [
placement: 'dropdown',
type: 'saveForLater',
},
+ {
+ Component: DefaultMessageActionComponents.dropdown.Flag,
+ placement: 'dropdown',
+ type: 'flag',
+ },
+ {
+ Component: DefaultMessageActionComponents.dropdown.Mute,
+ placement: 'dropdown',
+ type: 'mute',
+ },
+ {
+ Component: DefaultMessageActionComponents.dropdown.Delete,
+ placement: 'dropdown',
+ type: 'delete',
+ },
+ {
+ Component: DefaultMessageActionComponents.dropdown.BlockUser,
+ placement: 'dropdown',
+ type: 'blockUser',
+ },
] as const;
diff --git a/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts b/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts
index 96d12dc311..1c83cb3313 100644
--- a/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts
+++ b/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts
@@ -19,6 +19,7 @@ export const useBaseMessageActionSetFilter = (
const { initialMessage: isInitialMessage, message } = useMessageContext();
const { channelConfig } = useChannelStateContext();
const {
+ canBlockUser,
canDelete,
canEdit,
canFlag,
@@ -27,6 +28,7 @@ export const useBaseMessageActionSetFilter = (
canQuote,
canReact,
canReply,
+ canSendMessage,
} = useUserRole(message);
const isMessageThreadReply = typeof message.parent_id === 'string';
@@ -37,10 +39,8 @@ export const useBaseMessageActionSetFilter = (
if (
isInitialMessage || // not sure whether this thing even works anymore
!message.type ||
- message.type === 'error' ||
message.type === 'system' ||
message.type === 'ephemeral' ||
- message.status === 'failed' ||
message.status === 'sending'
)
return [];
@@ -50,7 +50,18 @@ export const useBaseMessageActionSetFilter = (
if (ACTIONS_NOT_WORKING_IN_THREAD.includes(type) && isMessageThreadReply)
return false;
+ // failed message menu has special treatment
+ if (message.status === 'failed' || message.type === 'error') {
+ return (
+ (type === 'resendMessage' && !canSendMessage) ||
+ (type === 'edit' && !canEdit) ||
+ (type === 'delete' && !canDelete)
+ );
+ }
+
if (
+ (type === 'blockUser' && !canBlockUser) ||
+ (type === 'copyMessageText' && !message.text) ||
(type === 'delete' && !canDelete) ||
(type === 'edit' && !canEdit) ||
(type === 'flag' && !canFlag) ||
@@ -67,6 +78,7 @@ export const useBaseMessageActionSetFilter = (
return true;
});
}, [
+ canBlockUser,
canDelete,
canEdit,
canFlag,
@@ -75,10 +87,12 @@ export const useBaseMessageActionSetFilter = (
canQuote,
canReact,
canReply,
+ canSendMessage,
channelConfig,
isInitialMessage,
isMessageThreadReply,
message.status,
+ message.text,
message.type,
disable,
messageActionSet,
diff --git a/src/components/MessageActions/styling/MessageActions.scss b/src/components/MessageActions/styling/MessageActions.scss
index 0611e9f621..b0179a2791 100644
--- a/src/components/MessageActions/styling/MessageActions.scss
+++ b/src/components/MessageActions/styling/MessageActions.scss
@@ -2,6 +2,13 @@
.str-chat__message-actions-box {
min-width: 180px;
+
+ .str-chat__context-menu__button.str-chat__message-actions-list-item-button--destructive {
+ svg,
+ .str-chat__context-menu__button__label {
+ color: var(--accent-error);
+ }
+ }
}
.str-chat__message-options {
diff --git a/src/i18n/de.json b/src/i18n/de.json
index f81194789d..2641fec627 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -41,34 +41,49 @@
"Anonymous poll": "Anonyme Umfrage",
"Archive": "Archivieren",
"aria/Attachment": "Anhang",
+ "aria/Block User": "Benutzer blockieren",
+ "aria/Bookmark Message": "Nachricht für später speichern",
"aria/Cancel Reply": "Antwort abbrechen",
"aria/Cancel upload": "Upload abbrechen",
"aria/Channel list": "Kanalliste",
"aria/Channel search results": "Kanalsuchergebnisse",
"aria/Close thread": "Thread schließen",
+ "aria/Copy Message Text": "Nachrichtentext kopieren",
+ "aria/Delete Message": "Nachricht löschen",
"aria/Download attachment": "Anhang herunterladen",
+ "aria/Edit Message": "Nachricht bearbeiten",
"aria/Emoji picker": "Emoji-Auswahl",
"aria/File input": "Dateieingabe",
"aria/File upload": "Datei hochladen",
+ "aria/Flag Message": "Nachricht melden",
"aria/Image input": "Bildeingabe",
"aria/Load More Channels": "Mehr Kanäle laden",
+ "aria/Mark Message Unread": "Als ungelesen markieren",
"aria/Menu": "Menü",
"aria/Message Options": "Nachrichtenoptionen",
+ "aria/Mute User": "Benutzer stummschalten",
"aria/Open Attachment Selector": "Anhang-Auswahl öffnen",
"aria/Open Menu": "Menü öffnen",
"aria/Open Message Actions Menu": "Nachrichtenaktionsmenü öffnen",
"aria/Open Reaction Selector": "Reaktionsauswahl öffnen",
"aria/Open Thread": "Thread öffnen",
+ "aria/Pin Message": "Nachricht anheften",
+ "aria/Quote Message": "Nachricht zitieren",
"aria/Reaction list": "Reaktionsliste",
+ "aria/Remind Me Message": "Erinnern",
"aria/Remind Me Options": "Erinnerungsoptionen",
"aria/Remove attachment": "Anhang entfernen",
"aria/Remove location attachment": "Standortanhang entfernen",
+ "aria/Resend Message": "Nachricht erneut senden",
"aria/Retry upload": "Upload erneut versuchen",
"aria/Search results": "Suchergebnisse",
"aria/Search results header filter button": "Suchergebnisse-Kopfzeilen-Filterbutton",
"aria/Send": "Senden",
"aria/Show preview": "Vorschau anzeigen",
"aria/Stop AI Generation": "KI-Generierung stoppen",
+ "aria/Unblock User": "Benutzer entsperren",
+ "aria/Unmute User": "Stummschaltung aufheben",
+ "aria/Unpin Message": "Anheftung aufheben",
"Ask a question": "Eine Frage stellen",
"Attach": "Anhängen",
"Attach files": "Dateien anhängen",
@@ -77,6 +92,7 @@
"Back": "Back",
"ban-command-args": "[@Benutzername] [Text]",
"ban-command-description": "Einen Benutzer verbannen",
+ "Block User": "Benutzer blockieren",
"Cancel": "Abbrechen",
"Cannot seek in the recording": "In der Aufnahme kann nicht gesucht werden",
"Channel Missing": "Kanal fehlt",
@@ -87,6 +103,7 @@
"Commands": "Befehle",
"Commands matching": "Übereinstimmende Befehle",
"Connection failure, reconnecting now...": "Verbindungsfehler, Wiederherstellung der Verbindung...",
+ "Copy Message": "Nachricht kopieren",
"Create": "Erstellen",
"Create poll": "Umfrage erstellen",
"Current location": "Aktueller Standort",
@@ -206,6 +223,7 @@
"Reply to Message": "Auf Nachricht antworten",
"replyCount_one": "1 Antwort",
"replyCount_other": "{{ count }} Antworten",
+ "Resend": "Erneut senden",
"Retry upload": "Upload erneut versuchen",
"Save for later": "Für später speichern",
"Saved for later": "Für später gespeichert",
@@ -253,6 +271,7 @@
"Thread": "Thread",
"Thread has not been found": "Thread wurde nicht gefunden",
"Thread reply": "Thread-Antwort",
+ "Thread Reply": "Thread-Antwort",
"Threads": "Diskussionen",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -269,6 +288,7 @@
"Unarchive": "Archivierung aufheben",
"unban-command-args": "[@Benutzername]",
"unban-command-description": "Einen Benutzer entbannen",
+ "Unblock User": "Benutzer entsperren",
"unknown error": "Unbekannter Fehler",
"Unmute": "Stummschaltung aufheben",
"unmute-command-args": "[@Benutzername]",
diff --git a/src/i18n/en.json b/src/i18n/en.json
index aa9a16201a..f6ece6b44f 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -41,34 +41,49 @@
"Anonymous poll": "Anonymous poll",
"Archive": "Archive",
"aria/Attachment": "Attachment",
+ "aria/Block User": "Block User",
+ "aria/Bookmark Message": "Bookmark Message",
"aria/Cancel Reply": "Cancel Reply",
"aria/Cancel upload": "Cancel upload",
"aria/Channel list": "Channel list",
"aria/Channel search results": "Channel search results",
"aria/Close thread": "Close thread",
+ "aria/Copy Message Text": "Copy Message Text",
+ "aria/Delete Message": "Delete Message",
"aria/Download attachment": "Download attachment",
+ "aria/Edit Message": "Edit Message",
"aria/Emoji picker": "Emoji picker",
"aria/File input": "File input",
"aria/File upload": "File upload",
+ "aria/Flag Message": "Flag Message",
"aria/Image input": "Image input",
"aria/Load More Channels": "Load More Channels",
+ "aria/Mark Message Unread": "Mark Message Unread",
"aria/Menu": "Menu",
"aria/Message Options": "Message Options",
+ "aria/Mute User": "Mute User",
"aria/Open Attachment Selector": "Open Attachment Selector",
"aria/Open Menu": "Open Menu",
"aria/Open Message Actions Menu": "Open Message Actions Menu",
"aria/Open Reaction Selector": "Open Reaction Selector",
"aria/Open Thread": "Open Thread",
+ "aria/Pin Message": "Pin Message",
+ "aria/Quote Message": "Quote Message",
"aria/Reaction list": "Reaction list",
+ "aria/Remind Me Message": "Remind Me Message",
"aria/Remind Me Options": "aria/Remind Me Options",
"aria/Remove attachment": "Remove attachment",
"aria/Remove location attachment": "Remove location attachment",
+ "aria/Resend Message": "Resend Message",
"aria/Retry upload": "Retry upload",
"aria/Search results": "Search results",
"aria/Search results header filter button": "Search results header filter button",
"aria/Send": "Send",
"aria/Show preview": "Show preview",
"aria/Stop AI Generation": "Stop AI Generation",
+ "aria/Unblock User": "Unblock User",
+ "aria/Unmute User": "Unmute User",
+ "aria/Unpin Message": "Unpin Message",
"Ask a question": "Ask a question",
"Attach": "Attach",
"Attach files": "Attach files",
@@ -77,6 +92,7 @@
"Back": "Back",
"ban-command-args": "[@username] [text]",
"ban-command-description": "Ban a user",
+ "Block User": "Block User",
"Cancel": "Cancel",
"Cannot seek in the recording": "Cannot seek in the recording",
"Channel Missing": "Channel Missing",
@@ -87,6 +103,7 @@
"Commands": "Commands",
"Commands matching": "Commands matching",
"Connection failure, reconnecting now...": "Connection failure, reconnecting now...",
+ "Copy Message": "Copy Message",
"Create": "Create",
"Create poll": "Create poll",
"Current location": "Current location",
@@ -206,6 +223,7 @@
"Reply to Message": "Reply to Message",
"replyCount_one": "1 reply",
"replyCount_other": "{{ count }} replies",
+ "Resend": "Resend",
"Retry upload": "Retry upload",
"Save for later": "Save for later",
"Saved for later": "Saved for later",
@@ -253,6 +271,7 @@
"Thread": "Thread",
"Thread has not been found": "Thread has not been found",
"Thread reply": "Thread reply",
+ "Thread Reply": "Thread Reply",
"Threads": "Threads",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -269,6 +288,7 @@
"Unarchive": "Unarchive",
"unban-command-args": "[@username]",
"unban-command-description": "Unban a user",
+ "Unblock User": "Unblock User",
"unknown error": "unknown error",
"Unmute": "Unmute",
"unmute-command-args": "[@username]",
diff --git a/src/i18n/es.json b/src/i18n/es.json
index e9a44f21f4..6efc585ba0 100644
--- a/src/i18n/es.json
+++ b/src/i18n/es.json
@@ -46,34 +46,49 @@
"Anonymous poll": "Encuesta anónima",
"Archive": "Archivo",
"aria/Attachment": "Adjunto",
+ "aria/Block User": "Bloquear usuario",
+ "aria/Bookmark Message": "Guardar mensaje",
"aria/Cancel Reply": "Cancelar respuesta",
"aria/Cancel upload": "Cancelar carga",
"aria/Channel list": "Lista de canales",
"aria/Channel search results": "Resultados de búsqueda de canales",
"aria/Close thread": "Cerrar hilo",
+ "aria/Copy Message Text": "Copiar texto del mensaje",
+ "aria/Delete Message": "Eliminar mensaje",
"aria/Download attachment": "Descargar adjunto",
+ "aria/Edit Message": "Editar mensaje",
"aria/Emoji picker": "Selector de emojis",
"aria/File input": "Entrada de archivo",
"aria/File upload": "Carga de archivo",
+ "aria/Flag Message": "Marcar mensaje",
"aria/Image input": "Entrada de imagen",
"aria/Load More Channels": "Cargar más canales",
+ "aria/Mark Message Unread": "Marcar como no leído",
"aria/Menu": "Menú",
"aria/Message Options": "Opciones de mensaje",
+ "aria/Mute User": "Silenciar usuario",
"aria/Open Attachment Selector": "Abrir selector de adjuntos",
"aria/Open Menu": "Abrir menú",
"aria/Open Message Actions Menu": "Abrir menú de acciones de mensaje",
"aria/Open Reaction Selector": "Abrir selector de reacciones",
"aria/Open Thread": "Abrir hilo",
+ "aria/Pin Message": "Fijar mensaje",
+ "aria/Quote Message": "Citar mensaje",
"aria/Reaction list": "Lista de reacciones",
+ "aria/Remind Me Message": "Recordarme",
"aria/Remind Me Options": "Opciones de recordatorio",
"aria/Remove attachment": "Eliminar adjunto",
"aria/Remove location attachment": "Eliminar adjunto de ubicación",
+ "aria/Resend Message": "Reenviar mensaje",
"aria/Retry upload": "Reintentar carga",
"aria/Search results": "Resultados de búsqueda",
"aria/Search results header filter button": "Botón de filtro del encabezado de resultados de búsqueda",
"aria/Send": "Enviar",
"aria/Show preview": "Mostrar vista previa",
"aria/Stop AI Generation": "Detener generación de IA",
+ "aria/Unblock User": "Desbloquear usuario",
+ "aria/Unmute User": "Activar sonido",
+ "aria/Unpin Message": "Desfijar mensaje",
"Ask a question": "Hacer una pregunta",
"Attach": "Adjuntar",
"Attach files": "Adjuntar archivos",
@@ -82,6 +97,7 @@
"Back": "Atrás",
"ban-command-args": "[@usuario] [texto]",
"ban-command-description": "Prohibir a un usuario",
+ "Block User": "Bloquear usuario",
"Cancel": "Cancelar",
"Cannot seek in the recording": "No se puede buscar en la grabación",
"Channel Missing": "Falta canal",
@@ -92,6 +108,7 @@
"Commands": "Comandos",
"Commands matching": "Coincidencia de comandos",
"Connection failure, reconnecting now...": "Fallo de conexión, reconectando ahora...",
+ "Copy Message": "Copiar mensaje",
"Create": "Crear",
"Create poll": "Crear encuesta",
"Current location": "Ubicación actual",
@@ -212,6 +229,7 @@
"replyCount_one": "1 respuesta",
"replyCount_many": "{{ count }} respuestas",
"replyCount_other": "{{ count }} respuestas",
+ "Resend": "Reenviar",
"Retry upload": "Reintentar la carga",
"Save for later": "Guardar para más tarde",
"Saved for later": "Guardado para más tarde",
@@ -262,6 +280,7 @@
"Thread": "Hilo",
"Thread has not been found": "No se ha encontrado el hilo",
"Thread reply": "Respuesta en hilo",
+ "Thread Reply": "Respuesta en hilo",
"Threads": "Hilos",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -278,6 +297,7 @@
"Unarchive": "Desarchivar",
"unban-command-args": "[@usuario]",
"unban-command-description": "Quitar la prohibición a un usuario",
+ "Unblock User": "Desbloquear usuario",
"unknown error": "error desconocido",
"Unmute": "Activar sonido",
"unmute-command-args": "[@usuario]",
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index a7a1d253a1..fc3169a512 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -46,34 +46,49 @@
"Anonymous poll": "Sondage anonyme",
"Archive": "Archiver",
"aria/Attachment": "Pièce jointe",
+ "aria/Block User": "Bloquer l'utilisateur",
+ "aria/Bookmark Message": "Enregistrer le message",
"aria/Cancel Reply": "Annuler la réponse",
"aria/Cancel upload": "Annuler le téléchargement",
"aria/Channel list": "Liste des canaux",
"aria/Channel search results": "Résultats de recherche de canaux",
"aria/Close thread": "Fermer le fil",
+ "aria/Copy Message Text": "Copier le texte du message",
+ "aria/Delete Message": "Supprimer le message",
"aria/Download attachment": "Télécharger la pièce jointe",
+ "aria/Edit Message": "Éditer un message",
"aria/Emoji picker": "Sélecteur d'émojis",
"aria/File input": "Entrée de fichier",
"aria/File upload": "Téléchargement de fichier",
+ "aria/Flag Message": "Signaler le message",
"aria/Image input": "Entrée d'image",
"aria/Load More Channels": "Charger plus de canaux",
+ "aria/Mark Message Unread": "Marquer comme non lu",
"aria/Menu": "Menu",
"aria/Message Options": "Options du message",
+ "aria/Mute User": "Mettre en sourdine",
"aria/Open Attachment Selector": "Ouvrir le sélecteur de pièces jointes",
"aria/Open Menu": "Ouvrir le menu",
"aria/Open Message Actions Menu": "Ouvrir le menu des actions du message",
"aria/Open Reaction Selector": "Ouvrir le sélecteur de réactions",
"aria/Open Thread": "Ouvrir le fil",
+ "aria/Pin Message": "Épingler le message",
+ "aria/Quote Message": "Citer le message",
"aria/Reaction list": "Liste des réactions",
+ "aria/Remind Me Message": "Me rappeler",
"aria/Remind Me Options": "Options de rappel",
"aria/Remove attachment": "Supprimer la pièce jointe",
"aria/Remove location attachment": "Supprimer la pièce jointe d'emplacement",
+ "aria/Resend Message": "Renvoyer le message",
"aria/Retry upload": "Réessayer le téléchargement",
"aria/Search results": "Résultats de recherche",
"aria/Search results header filter button": "Bouton de filtre d'en-tête des résultats de recherche",
"aria/Send": "Envoyer",
"aria/Show preview": "Afficher l'aperçu",
"aria/Stop AI Generation": "Arrêter la génération d'IA",
+ "aria/Unblock User": "Débloquer l'utilisateur",
+ "aria/Unmute User": "Désactiver muet",
+ "aria/Unpin Message": "Détacher le message",
"Ask a question": "Poser une question",
"Attach": "Joindre",
"Attach files": "Joindre des fichiers",
@@ -82,6 +97,7 @@
"Back": "Retour",
"ban-command-args": "[@nomdutilisateur] [texte]",
"ban-command-description": "Bannir un utilisateur",
+ "Block User": "Bloquer l'utilisateur",
"Cancel": "Annuler",
"Cannot seek in the recording": "Impossible de rechercher dans l'enregistrement",
"Channel Missing": "Canal Manquant",
@@ -92,6 +108,7 @@
"Commands": "Commandes",
"Commands matching": "Correspondance des commandes",
"Connection failure, reconnecting now...": "Échec de la connexion, reconnexion en cours...",
+ "Copy Message": "Copier le message",
"Create": "Créer",
"Create poll": "Créer un sondage",
"Current location": "Emplacement actuel",
@@ -212,6 +229,7 @@
"replyCount_one": "1 réponse",
"replyCount_many": "{{ count }} réponses",
"replyCount_other": "{{ count }} réponses",
+ "Resend": "Renvoyer",
"Retry upload": "Réessayer le téléchargement",
"Save for later": "Enregistrer pour plus tard",
"Saved for later": "Enregistré pour plus tard",
@@ -262,6 +280,7 @@
"Thread": "Fil de discussion",
"Thread has not been found": "Le fil de discussion n'a pas été trouvé",
"Thread reply": "Réponse dans le fil",
+ "Thread Reply": "Réponse dans le fil",
"Threads": "Fils",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -278,6 +297,7 @@
"Unarchive": "Désarchiver",
"unban-command-args": "[@nomdutilisateur]",
"unban-command-description": "Débannir un utilisateur",
+ "Unblock User": "Débloquer l'utilisateur",
"unknown error": "erreur inconnue",
"Unmute": "Désactiver muet",
"unmute-command-args": "[@nomdutilisateur]",
diff --git a/src/i18n/hi.json b/src/i18n/hi.json
index f955595eea..5ec287262d 100644
--- a/src/i18n/hi.json
+++ b/src/i18n/hi.json
@@ -41,34 +41,49 @@
"Anonymous poll": "गुमनाम मतदान",
"Archive": "आर्काइव",
"aria/Attachment": "अनुलग्नक",
+ "aria/Block User": "उपयोगकर्ता को ब्लॉक करें",
+ "aria/Bookmark Message": "संदेश बुकमार्क करें",
"aria/Cancel Reply": "उत्तर रद्द करें",
"aria/Cancel upload": "अपलोड रद्द करें",
"aria/Channel list": "चैनल सूची",
"aria/Channel search results": "चैनल खोज परिणाम",
"aria/Close thread": "थ्रेड बंद करें",
+ "aria/Copy Message Text": "संदेश की टेक्स्ट कॉपी करें",
+ "aria/Delete Message": "संदेश डिलीट करें",
"aria/Download attachment": "अनुलग्नक डाउनलोड करें",
+ "aria/Edit Message": "मैसेज में बदलाव करे",
"aria/Emoji picker": "इमोजी चुनने वाला",
"aria/File input": "फ़ाइल इनपुट",
"aria/File upload": "फ़ाइल अपलोड",
+ "aria/Flag Message": "संदेश फ्लैग करें",
"aria/Image input": "छवि इनपुट",
"aria/Load More Channels": "और चैनल लोड करें",
+ "aria/Mark Message Unread": "अपठित चिह्नित करें",
"aria/Menu": "मेन्यू",
"aria/Message Options": "संदेश विकल्प",
+ "aria/Mute User": "उपयोगकर्ता म्यूट करें",
"aria/Open Attachment Selector": "अटैचमेंट चयनकर्ता खोलें",
"aria/Open Menu": "मेन्यू खोलें",
"aria/Open Message Actions Menu": "संदेश क्रिया मेन्यू खोलें",
"aria/Open Reaction Selector": "प्रतिक्रिया चयनकर्ता खोलें",
"aria/Open Thread": "थ्रेड खोलें",
+ "aria/Pin Message": "संदेश पिन करें",
+ "aria/Quote Message": "संदेश उद्धरण",
"aria/Reaction list": "प्रतिक्रिया सूची",
+ "aria/Remind Me Message": "मुझे याद दिलाएं",
"aria/Remind Me Options": "रिमाइंडर विकल्प",
"aria/Remove attachment": "संलग्नक हटाएं",
"aria/Remove location attachment": "स्थान संलग्नक हटाएं",
+ "aria/Resend Message": "संदेश फिर से भेजें",
"aria/Retry upload": "अपलोड पुनः प्रयास करें",
"aria/Search results": "खोज परिणाम",
"aria/Search results header filter button": "खोज परिणाम हेडर फ़िल्टर बटन",
"aria/Send": "भेजें",
"aria/Show preview": "पूर्वावलोकन दिखाएं",
"aria/Stop AI Generation": "एआई जनरेशन रोकें",
+ "aria/Unblock User": "उपयोगकर्ता अनब्लॉक करें",
+ "aria/Unmute User": "उपयोगकर्ता अनम्यूट करें",
+ "aria/Unpin Message": "संदेश अनपिन करें",
"Ask a question": "एक प्रश्न पूछें",
"Attach": "संलग्न करें",
"Attach files": "फाइल्स अटैच करे",
@@ -77,6 +92,7 @@
"Back": "वापस",
"ban-command-args": "[@उपयोगकर्तनाम] [पाठ]",
"ban-command-description": "एक उपयोगकर्ता को प्रतिषेधित करें",
+ "Block User": "उपयोगकर्ता को ब्लॉक करें",
"Cancel": "रद्द करें",
"Cannot seek in the recording": "रेकॉर्डिंग में खोज नहीं की जा सकती",
"Channel Missing": "चैनल उपलब्ध नहीं है",
@@ -87,6 +103,7 @@
"Commands": "कमांड",
"Commands matching": "मेल खाती है",
"Connection failure, reconnecting now...": "कनेक्शन विफल रहा, अब पुनः कनेक्ट हो रहा है ...",
+ "Copy Message": "संदेश कॉपी करें",
"Create": "बनाएँ",
"Create poll": "मतदान बनाएँ",
"Current location": "वर्तमान स्थान",
@@ -207,6 +224,7 @@
"Reply to Message": "संदेश का जवाब दें",
"replyCount_one": "1 रिप्लाई",
"replyCount_other": "{{ count }} रिप्लाई",
+ "Resend": "फिर से भेजें",
"Retry upload": "अपलोड फिर से करें",
"Save for later": "बाद के लिए सहेजें",
"Saved for later": "बाद के लिए सहेजा गया",
@@ -254,6 +272,7 @@
"Thread": "रिप्लाई थ्रेड",
"Thread has not been found": "थ्रेड नहीं मिला",
"Thread reply": "थ्रेड में उत्तर",
+ "Thread Reply": "थ्रेड में उत्तर",
"Threads": "थ्रेड्स",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -270,6 +289,7 @@
"Unarchive": "अनआर्काइव",
"unban-command-args": "[@उपयोगकर्तनाम]",
"unban-command-description": "एक उपयोगकर्ता को प्रतिषेध से मुक्त करें",
+ "Unblock User": "उपयोगकर्ता अनब्लॉक करें",
"unknown error": "अज्ञात त्रुटि",
"Unmute": "अनम्यूट",
"unmute-command-args": "[@उपयोगकर्तनाम]",
diff --git a/src/i18n/it.json b/src/i18n/it.json
index d76ff3ec07..29d2a8dbdc 100644
--- a/src/i18n/it.json
+++ b/src/i18n/it.json
@@ -46,34 +46,49 @@
"Anonymous poll": "Sondaggio anonimo",
"Archive": "Archivia",
"aria/Attachment": "Allegato",
+ "aria/Block User": "Blocca utente",
+ "aria/Bookmark Message": "Salva messaggio",
"aria/Cancel Reply": "Annulla risposta",
"aria/Cancel upload": "Annulla caricamento",
"aria/Channel list": "Elenco dei canali",
"aria/Channel search results": "Risultati della ricerca dei canali",
"aria/Close thread": "Chiudi discussione",
+ "aria/Copy Message Text": "Copia testo messaggio",
+ "aria/Delete Message": "Elimina messaggio",
"aria/Download attachment": "Scarica l'allegato",
+ "aria/Edit Message": "Modifica messaggio",
"aria/Emoji picker": "Selettore di emoji",
"aria/File input": "Input di file",
"aria/File upload": "Caricamento di file",
+ "aria/Flag Message": "Segnala messaggio",
"aria/Image input": "Input di immagine",
"aria/Load More Channels": "Carica altri canali",
+ "aria/Mark Message Unread": "Contrassegna come non letto",
"aria/Menu": "Menu",
"aria/Message Options": "Opzioni di messaggio",
+ "aria/Mute User": "Mute utente",
"aria/Open Attachment Selector": "Apri selettore allegati",
"aria/Open Menu": "Apri menu",
"aria/Open Message Actions Menu": "Apri il menu delle azioni di messaggio",
"aria/Open Reaction Selector": "Apri il selettore di reazione",
"aria/Open Thread": "Apri discussione",
+ "aria/Pin Message": "Appunta messaggio",
+ "aria/Quote Message": "Citazione messaggio",
"aria/Reaction list": "Elenco delle reazioni",
+ "aria/Remind Me Message": "Ricordami",
"aria/Remind Me Options": "Opzioni promemoria",
"aria/Remove attachment": "Rimuovi allegato",
"aria/Remove location attachment": "Rimuovi allegato posizione",
+ "aria/Resend Message": "Invia di nuovo messaggio",
"aria/Retry upload": "Riprova caricamento",
"aria/Search results": "Risultati della ricerca",
"aria/Search results header filter button": "Pulsante filtro intestazione risultati ricerca",
"aria/Send": "Invia",
"aria/Show preview": "Mostra anteprima",
"aria/Stop AI Generation": "Interrompi generazione IA",
+ "aria/Unblock User": "Sblocca utente",
+ "aria/Unmute User": "Riattiva il notifiche",
+ "aria/Unpin Message": "Rimuovi messaggio appuntato",
"Ask a question": "Fai una domanda",
"Attach": "Allega",
"Attach files": "Allega file",
@@ -82,6 +97,7 @@
"Back": "Indietro",
"ban-command-args": "[@nomeutente] [testo]",
"ban-command-description": "Vietare un utente",
+ "Block User": "Blocca utente",
"Cancel": "Annulla",
"Cannot seek in the recording": "Impossibile cercare nella registrazione",
"Channel Missing": "Il canale non esiste",
@@ -92,6 +108,7 @@
"Commands": "Comandi",
"Commands matching": "Comandi corrispondenti",
"Connection failure, reconnecting now...": "Errore di connessione, riconnessione in corso...",
+ "Copy Message": "Copia messaggio",
"Create": "Crea",
"Create poll": "Crea sondaggio",
"Current location": "Posizione attuale",
@@ -212,6 +229,7 @@
"replyCount_one": "Una risposta",
"replyCount_many": "{{ count }} risposte",
"replyCount_other": "{{ count }} risposte",
+ "Resend": "Invia di nuovo",
"Retry upload": "Riprova caricamento",
"Save for later": "Salva per dopo",
"Saved for later": "Salvato per dopo",
@@ -262,6 +280,7 @@
"Thread": "Discussione",
"Thread has not been found": "Discussione non trovata",
"Thread reply": "Risposta nella discussione",
+ "Thread Reply": "Risposta nella discussione",
"Threads": "Thread",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -278,6 +297,7 @@
"Unarchive": "Ripristina",
"unban-command-args": "[@nomeutente]",
"unban-command-description": "Togliere il divieto a un utente",
+ "Unblock User": "Sblocca utente",
"unknown error": "errore sconosciuto",
"Unmute": "Riattiva il notifiche",
"unmute-command-args": "[@nomeutente]",
diff --git a/src/i18n/ja.json b/src/i18n/ja.json
index 97e26d1290..d82ab8ba75 100644
--- a/src/i18n/ja.json
+++ b/src/i18n/ja.json
@@ -41,34 +41,49 @@
"Anonymous poll": "匿名投票",
"Archive": "アーカイブ",
"aria/Attachment": "添付ファイル",
+ "aria/Block User": "ユーザーをブロック",
+ "aria/Bookmark Message": "メッセージをブックマーク",
"aria/Cancel Reply": "返信をキャンセル",
"aria/Cancel upload": "アップロードをキャンセル",
"aria/Channel list": "チャンネル一覧",
"aria/Channel search results": "チャンネル検索結果",
"aria/Close thread": "スレッドを閉じる",
+ "aria/Copy Message Text": "メッセージテキストをコピー",
+ "aria/Delete Message": "メッセージを削除",
"aria/Download attachment": "添付ファイルをダウンロード",
+ "aria/Edit Message": "メッセージを編集",
"aria/Emoji picker": "絵文字ピッカー",
"aria/File input": "ファイル入力",
"aria/File upload": "ファイルアップロード",
+ "aria/Flag Message": "メッセージをフラグ",
"aria/Image input": "画像入力",
"aria/Load More Channels": "さらにチャンネルを読み込む",
+ "aria/Mark Message Unread": "未読としてマーク",
"aria/Menu": "メニュー",
"aria/Message Options": "メッセージオプション",
+ "aria/Mute User": "ユーザーをミュート",
"aria/Open Attachment Selector": "添付ファイル選択を開く",
"aria/Open Menu": "メニューを開く",
"aria/Open Message Actions Menu": "メッセージアクションメニューを開く",
"aria/Open Reaction Selector": "リアクションセレクターを開く",
"aria/Open Thread": "スレッドを開く",
+ "aria/Pin Message": "メッセージをピン",
+ "aria/Quote Message": "メッセージを引用",
"aria/Reaction list": "リアクション一覧",
+ "aria/Remind Me Message": "リマインダー",
"aria/Remind Me Options": "リマインダーオプション",
"aria/Remove attachment": "添付ファイルを削除",
"aria/Remove location attachment": "位置情報の添付ファイルを削除",
+ "aria/Resend Message": "メッセージを再送信",
"aria/Retry upload": "アップロードを再試行",
"aria/Search results": "検索結果",
"aria/Search results header filter button": "検索結果ヘッダーフィルターボタン",
"aria/Send": "送信",
"aria/Show preview": "プレビューを表示",
"aria/Stop AI Generation": "AI生成を停止",
+ "aria/Unblock User": "ユーザーのブロックを解除",
+ "aria/Unmute User": "無音を解除する",
+ "aria/Unpin Message": "ピンを解除",
"Ask a question": "質問する",
"Attach": "添付",
"Attach files": "ファイルを添付する",
@@ -77,6 +92,7 @@
"Back": "戻る",
"ban-command-args": "[@ユーザ名] [テキスト]",
"ban-command-description": "ユーザーを禁止する",
+ "Block User": "ユーザーをブロック",
"Cancel": "キャンセル",
"Cannot seek in the recording": "録音中にシークできません",
"Channel Missing": "チャネルがありません",
@@ -87,6 +103,7 @@
"Commands": "コマンド",
"Commands matching": "一致するコマンド",
"Connection failure, reconnecting now...": "接続が失敗しました。再接続中...",
+ "Copy Message": "メッセージをコピー",
"Create": "作成",
"Create poll": "投票を作成",
"Current location": "現在の位置",
@@ -206,6 +223,7 @@
"Reply to Message": "メッセージに返信",
"replyCount_one": "1件の返信",
"replyCount_other": "{{ count }} 返信",
+ "Resend": "再送信",
"Retry upload": "アップロードを再試行",
"Save for later": "後で保存",
"Saved for later": "後で保存済み",
@@ -253,6 +271,7 @@
"Thread": "スレッド",
"Thread has not been found": "スレッドが見つかりませんでした",
"Thread reply": "スレッドの返信",
+ "Thread Reply": "スレッドの返信",
"Threads": "スレッド",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -269,6 +288,7 @@
"Unarchive": "アーカイブ解除",
"unban-command-args": "[@ユーザ名]",
"unban-command-description": "ユーザーの禁止を解除する",
+ "Unblock User": "ユーザーのブロックを解除",
"unknown error": "不明なエラー",
"Unmute": "無音を解除する",
"unmute-command-args": "[@ユーザ名]",
diff --git a/src/i18n/ko.json b/src/i18n/ko.json
index d2b0aff4c9..03ce7876fd 100644
--- a/src/i18n/ko.json
+++ b/src/i18n/ko.json
@@ -41,34 +41,49 @@
"Anonymous poll": "익명 투표",
"Archive": "아카이브",
"aria/Attachment": "첨부 파일",
+ "aria/Block User": "사용자 차단",
+ "aria/Bookmark Message": "메시지 북마크",
"aria/Cancel Reply": "답장 취소",
"aria/Cancel upload": "업로드 취소",
"aria/Channel list": "채널 목록",
"aria/Channel search results": "채널 검색 결과",
"aria/Close thread": "스레드 닫기",
+ "aria/Copy Message Text": "메시지 텍스트 복사",
+ "aria/Delete Message": "메시지 삭제",
"aria/Download attachment": "첨부 파일 다운로드",
+ "aria/Edit Message": "메시지 수정",
"aria/Emoji picker": "이모지 선택기",
"aria/File input": "파일 입력",
"aria/File upload": "파일 업로드",
+ "aria/Flag Message": "메시지 신고",
"aria/Image input": "이미지 입력",
"aria/Load More Channels": "더 많은 채널 불러오기",
+ "aria/Mark Message Unread": "읽지 않음으로 표시",
"aria/Menu": "메뉴",
"aria/Message Options": "메시지 옵션",
+ "aria/Mute User": "사용자 음소거",
"aria/Open Attachment Selector": "첨부 파일 선택기 열기",
"aria/Open Menu": "메뉴 열기",
"aria/Open Message Actions Menu": "메시지 액션 메뉴 열기",
"aria/Open Reaction Selector": "반응 선택기 열기",
"aria/Open Thread": "스레드 열기",
+ "aria/Pin Message": "메시지 고정",
+ "aria/Quote Message": "메시지 인용",
"aria/Reaction list": "반응 목록",
+ "aria/Remind Me Message": "알림 설정",
"aria/Remind Me Options": "알림 옵션",
"aria/Remove attachment": "첨부 파일 제거",
"aria/Remove location attachment": "위치 첨부 파일 제거",
+ "aria/Resend Message": "메시지 다시 보내기",
"aria/Retry upload": "업로드 다시 시도",
"aria/Search results": "검색 결과",
"aria/Search results header filter button": "검색 결과 헤더 필터 버튼",
"aria/Send": "보내기",
"aria/Show preview": "미리보기 표시",
"aria/Stop AI Generation": "AI 생성 중지",
+ "aria/Unblock User": "사용자 차단 해제",
+ "aria/Unmute User": "음소거 해제",
+ "aria/Unpin Message": "핀 해제",
"Ask a question": "질문하기",
"Attach": "첨부",
"Attach files": "파일 첨부",
@@ -77,6 +92,7 @@
"Back": "뒤로",
"ban-command-args": "[@사용자이름] [텍스트]",
"ban-command-description": "사용자를 차단",
+ "Block User": "사용자 차단",
"Cancel": "취소",
"Cannot seek in the recording": "녹음에서 찾을 수 없습니다",
"Channel Missing": "채널 누락",
@@ -87,6 +103,7 @@
"Commands": "명령어",
"Commands matching": "일치하는 명령",
"Connection failure, reconnecting now...": "연결 실패, 지금 다시 연결 중...",
+ "Copy Message": "메시지 복사",
"Create": "생성",
"Create poll": "투표 생성",
"Current location": "현재 위치",
@@ -206,6 +223,7 @@
"Reply to Message": "메시지에 답장",
"replyCount_one": "답장 1개",
"replyCount_other": "{{ count }} 답장",
+ "Resend": "다시 보내기",
"Retry upload": "업로드 다시 시도",
"Save for later": "나중에 저장",
"Saved for later": "나중에 저장됨",
@@ -253,6 +271,7 @@
"Thread": "스레드",
"Thread has not been found": "스레드를 찾을 수 없습니다",
"Thread reply": "스레드 답장",
+ "Thread Reply": "스레드 답장",
"Threads": "스레드",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -269,6 +288,7 @@
"Unarchive": "아카이브 해제",
"unban-command-args": "[@사용자이름]",
"unban-command-description": "사용자 차단 해제",
+ "Unblock User": "사용자 차단 해제",
"unknown error": "알 수 없는 오류",
"Unmute": "음소거 해제",
"unmute-command-args": "[@사용자이름]",
diff --git a/src/i18n/nl.json b/src/i18n/nl.json
index 4c11da0fa2..f87aa0d76c 100644
--- a/src/i18n/nl.json
+++ b/src/i18n/nl.json
@@ -41,34 +41,49 @@
"Anonymous poll": "Anonieme peiling",
"Archive": "Archief",
"aria/Attachment": "Bijlage",
+ "aria/Block User": "Gebruiker blokkeren",
+ "aria/Bookmark Message": "Bericht bookmarken",
"aria/Cancel Reply": "Antwoord annuleren",
"aria/Cancel upload": "Upload annuleren",
"aria/Channel list": "Kanaallijst",
"aria/Channel search results": "Zoekresultaten voor kanalen",
"aria/Close thread": "Draad sluiten",
+ "aria/Copy Message Text": "Berichttekst kopiëren",
+ "aria/Delete Message": "Bericht verwijderen",
"aria/Download attachment": "Bijlage downloaden",
+ "aria/Edit Message": "Bericht bewerken",
"aria/Emoji picker": "Emoji kiezer",
"aria/File input": "Bestandsinvoer",
"aria/File upload": "Bestand uploaden",
+ "aria/Flag Message": "Bericht markeren",
"aria/Image input": "Afbeelding invoeren",
"aria/Load More Channels": "Meer kanalen laden",
+ "aria/Mark Message Unread": "Markeren als ongelezen",
"aria/Menu": "Menu",
"aria/Message Options": "Berichtopties",
+ "aria/Mute User": "Gebruiker dempen",
"aria/Open Attachment Selector": "Open bijlage selector",
"aria/Open Menu": "Menu openen",
"aria/Open Message Actions Menu": "Menu voor berichtacties openen",
"aria/Open Reaction Selector": "Reactiekiezer openen",
"aria/Open Thread": "Draad openen",
+ "aria/Pin Message": "Bericht vastmaken",
+ "aria/Quote Message": "Bericht citeren",
"aria/Reaction list": "Reactielijst",
+ "aria/Remind Me Message": "Herinner mij",
"aria/Remind Me Options": "Herinneringsopties",
"aria/Remove attachment": "Bijlage verwijderen",
"aria/Remove location attachment": "Locatie bijlage verwijderen",
+ "aria/Resend Message": "Bericht opnieuw verzenden",
"aria/Retry upload": "Upload opnieuw proberen",
"aria/Search results": "Zoekresultaten",
"aria/Search results header filter button": "Zoekresultaten header filter knop",
"aria/Send": "Verzenden",
"aria/Show preview": "Voorbeeld tonen",
"aria/Stop AI Generation": "AI-generatie stoppen",
+ "aria/Unblock User": "Gebruiker deblokkeren",
+ "aria/Unmute User": "Dempen opheffen",
+ "aria/Unpin Message": "Losmaken",
"Ask a question": "Stel een vraag",
"Attach": "Bijvoegen",
"Attach files": "Bijlage toevoegen",
@@ -77,6 +92,7 @@
"Back": "Terug",
"ban-command-args": "[@gebruikersnaam] [tekst]",
"ban-command-description": "Een gebruiker verbannen",
+ "Block User": "Gebruiker blokkeren",
"Cancel": "Annuleer",
"Cannot seek in the recording": "Kan niet zoeken in de opname",
"Channel Missing": "Kanaal niet gevonden",
@@ -87,6 +103,7 @@
"Commands": "Commando's",
"Commands matching": "Bijpassende opdrachten",
"Connection failure, reconnecting now...": "Verbindingsfout, opnieuw verbinden...",
+ "Copy Message": "Bericht kopiëren",
"Create": "Maak",
"Create poll": "Maak peiling",
"Current location": "Huidige locatie",
@@ -206,6 +223,7 @@
"Reply to Message": "Antwoord op bericht",
"replyCount_one": "1 antwoord",
"replyCount_other": "{{ count }} antwoorden",
+ "Resend": "Opnieuw verzenden",
"Retry upload": "Upload opnieuw proberen",
"Save for later": "Bewaren voor later",
"Saved for later": "Bewaard voor later",
@@ -255,6 +273,7 @@
"Thread": "Draadje",
"Thread has not been found": "Draadje niet gevonden",
"Thread reply": "Draadje antwoord",
+ "Thread Reply": "Draadje antwoord",
"Threads": "Discussies",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -271,6 +290,7 @@
"Unarchive": "Uit archief halen",
"unban-command-args": "[@gebruikersnaam]",
"unban-command-description": "Een gebruiker debannen",
+ "Unblock User": "Gebruiker deblokkeren",
"unknown error": "onbekende fout",
"Unmute": "Dempen opheffen",
"unmute-command-args": "[@gebruikersnaam]",
diff --git a/src/i18n/pt.json b/src/i18n/pt.json
index 9197a60b53..f7833f819d 100644
--- a/src/i18n/pt.json
+++ b/src/i18n/pt.json
@@ -46,34 +46,49 @@
"Anonymous poll": "Enquete anônima",
"Archive": "Arquivar",
"aria/Attachment": "Anexo",
+ "aria/Block User": "Bloquear usuário",
+ "aria/Bookmark Message": "Marcar mensagem",
"aria/Cancel Reply": "Cancelar resposta",
"aria/Cancel upload": "Cancelar upload",
"aria/Channel list": "Lista de canais",
"aria/Channel search results": "Resultados de pesquisa de canais",
"aria/Close thread": "Fechar tópico",
+ "aria/Copy Message Text": "Copiar texto da mensagem",
+ "aria/Delete Message": "Excluir mensagem",
"aria/Download attachment": "Baixar anexo",
+ "aria/Edit Message": "Editar Mensagem",
"aria/Emoji picker": "Seletor de emojis",
"aria/File input": "Entrada de arquivo",
"aria/File upload": "Carregar arquivo",
+ "aria/Flag Message": "Reportar mensagem",
"aria/Image input": "Entrada de imagem",
"aria/Load More Channels": "Carregar mais canais",
+ "aria/Mark Message Unread": "Marcar como não lida",
"aria/Menu": "Menu",
"aria/Message Options": "Opções de mensagem",
+ "aria/Mute User": "Silenciar usuário",
"aria/Open Attachment Selector": "Abrir seletor de anexos",
"aria/Open Menu": "Abrir menu",
"aria/Open Message Actions Menu": "Abrir menu de ações de mensagem",
"aria/Open Reaction Selector": "Abrir seletor de reações",
"aria/Open Thread": "Abrir tópico",
+ "aria/Pin Message": "Fixar mensagem",
+ "aria/Quote Message": "Citar mensagem",
"aria/Reaction list": "Lista de reações",
+ "aria/Remind Me Message": "Lembrar-me",
"aria/Remind Me Options": "Opções de lembrete",
"aria/Remove attachment": "Remover anexo",
"aria/Remove location attachment": "Remover anexo de localização",
+ "aria/Resend Message": "Reenviar mensagem",
"aria/Retry upload": "Tentar upload novamente",
"aria/Search results": "Resultados da pesquisa",
"aria/Search results header filter button": "Botão de filtro do cabeçalho dos resultados da pesquisa",
"aria/Send": "Enviar",
"aria/Show preview": "Mostrar prévia",
"aria/Stop AI Generation": "Parar geração de IA",
+ "aria/Unblock User": "Desbloquear usuário",
+ "aria/Unmute User": "Ativar som",
+ "aria/Unpin Message": "Desfixar mensagem",
"Ask a question": "Faça uma pergunta",
"Attach": "Anexar",
"Attach files": "Anexar arquivos",
@@ -82,6 +97,7 @@
"Back": "Voltar",
"ban-command-args": "[@nomedeusuário] [texto]",
"ban-command-description": "Banir um usuário",
+ "Block User": "Bloquear usuário",
"Cancel": "Cancelar",
"Cannot seek in the recording": "Não é possível buscar na gravação",
"Channel Missing": "Canal ausente",
@@ -92,6 +108,7 @@
"Commands": "Comandos",
"Commands matching": "Comandos correspondentes",
"Connection failure, reconnecting now...": "Falha de conexão, reconectando agora...",
+ "Copy Message": "Copiar mensagem",
"Create": "Criar",
"Create poll": "Criar enquete",
"Current location": "Localização atual",
@@ -212,6 +229,7 @@
"replyCount_one": "1 resposta",
"replyCount_many": "{{ count }} respostas",
"replyCount_other": "{{ count }} respostas",
+ "Resend": "Reenviar",
"Retry upload": "Tentar enviar novamente",
"Save for later": "Salvar para depois",
"Saved for later": "Salvo para depois",
@@ -262,6 +280,7 @@
"Thread": "Fio",
"Thread has not been found": "Fio não encontrado",
"Thread reply": "Resposta no fio",
+ "Thread Reply": "Resposta no fio",
"Threads": "Tópicos",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -278,6 +297,7 @@
"Unarchive": "Desarquivar",
"unban-command-args": "[@nomedeusuário]",
"unban-command-description": "Desbanir um usuário",
+ "Unblock User": "Desbloquear usuário",
"unknown error": "erro desconhecido",
"Unmute": "Ativar som",
"unmute-command-args": "[@nomedeusuário]",
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index 1f6033bebf..e3d4dd9986 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -51,34 +51,49 @@
"Anonymous poll": "Анонимный опрос",
"Archive": "Aрхивировать",
"aria/Attachment": "Вложение",
+ "aria/Block User": "Заблокировать пользователя",
+ "aria/Bookmark Message": "Сохранить сообщение",
"aria/Cancel Reply": "Отменить ответ",
"aria/Cancel upload": "Отменить загрузку",
"aria/Channel list": "Список каналов",
"aria/Channel search results": "Результаты поиска по каналам",
"aria/Close thread": "Закрыть тему",
+ "aria/Copy Message Text": "Копировать текст сообщения",
+ "aria/Delete Message": "Удалить сообщение",
"aria/Download attachment": "Скачать вложение",
+ "aria/Edit Message": "Редактировать сообщение",
"aria/Emoji picker": "Выбор эмодзи",
"aria/File input": "Ввод файла",
"aria/File upload": "Загрузка файла",
+ "aria/Flag Message": "Пожаловаться на сообщение",
"aria/Image input": "Ввод изображения",
"aria/Load More Channels": "Загрузить больше каналов",
+ "aria/Mark Message Unread": "Отметить как непрочитанное",
"aria/Menu": "Меню",
"aria/Message Options": "Параметры сообщения",
+ "aria/Mute User": "Отключить уведомления",
"aria/Open Attachment Selector": "Открыть выбор вложений",
"aria/Open Menu": "Открыть меню",
"aria/Open Message Actions Menu": "Открыть меню действий с сообщениями",
"aria/Open Reaction Selector": "Открыть селектор реакций",
"aria/Open Thread": "Открыть тему",
+ "aria/Pin Message": "Закрепить сообщение",
+ "aria/Quote Message": "Цитировать сообщение",
"aria/Reaction list": "Список реакций",
+ "aria/Remind Me Message": "Напомнить мне",
"aria/Remind Me Options": "Параметры напоминания",
"aria/Remove attachment": "Удалить вложение",
"aria/Remove location attachment": "Удалить вложение местоположения",
+ "aria/Resend Message": "Отправить сообщение повторно",
"aria/Retry upload": "Повторить загрузку",
"aria/Search results": "Результаты поиска",
"aria/Search results header filter button": "Кнопка фильтра заголовка результатов поиска",
"aria/Send": "Отправить",
"aria/Show preview": "Показать предпросмотр",
"aria/Stop AI Generation": "Остановить генерацию ИИ",
+ "aria/Unblock User": "Разблокировать пользователя",
+ "aria/Unmute User": "Включить уведомления",
+ "aria/Unpin Message": "Открепить сообщение",
"Ask a question": "Задать вопрос",
"Attach": "Прикрепить",
"Attach files": "Прикрепить файлы",
@@ -87,6 +102,7 @@
"Back": "Назад",
"ban-command-args": "[@имяпользователя] [текст]",
"ban-command-description": "Заблокировать пользователя",
+ "Block User": "Заблокировать пользователя",
"Cancel": "Отмена",
"Cannot seek in the recording": "Невозможно осуществить поиск в записи",
"Channel Missing": "Канал не найден",
@@ -97,6 +113,7 @@
"Commands": "Команды",
"Commands matching": "Соответствие команд",
"Connection failure, reconnecting now...": "Ошибка соединения, переподключение...",
+ "Copy Message": "Копировать сообщение",
"Create": "Создать",
"Create poll": "Создать опрос",
"Current location": "Текущее местоположение",
@@ -218,6 +235,7 @@
"replyCount_few": "{{ count }} ответов",
"replyCount_many": "{{ count }} ответов",
"replyCount_other": "{{ count }} ответов",
+ "Resend": "Отправить повторно",
"Retry upload": "Повторить загрузку",
"Save for later": "Сохранить на потом",
"Saved for later": "Сохранено на потом",
@@ -271,6 +289,7 @@
"Thread": "Ветка",
"Thread has not been found": "Ветка не найдена",
"Thread reply": "Ответ в ветке",
+ "Thread Reply": "Ответ в ветке",
"Threads": "Треды",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -287,6 +306,7 @@
"Unarchive": "Удалить из архива",
"unban-command-args": "[@имяпользователя]",
"unban-command-description": "Разблокировать пользователя",
+ "Unblock User": "Разблокировать пользователя",
"unknown error": "неизвестная ошибка",
"Unmute": "Включить уведомления",
"unmute-command-args": "[@имяпользователя]",
diff --git a/src/i18n/tr.json b/src/i18n/tr.json
index 44c8ae7cbd..e2689aeabf 100644
--- a/src/i18n/tr.json
+++ b/src/i18n/tr.json
@@ -41,34 +41,49 @@
"Anonymous poll": "Anonim anket",
"Archive": "Arşivle",
"aria/Attachment": "Ek",
+ "aria/Block User": "Kullanıcıyı engelle",
+ "aria/Bookmark Message": "Mesajı yer imi ekle",
"aria/Cancel Reply": "Cevabı İptal Et",
"aria/Cancel upload": "Yüklemeyi İptal Et",
"aria/Channel list": "Kanal listesi",
"aria/Channel search results": "Kanal arama sonuçları",
"aria/Close thread": "Konuyu kapat",
+ "aria/Copy Message Text": "Mesaj metnini kopyala",
+ "aria/Delete Message": "Mesajı sil",
"aria/Download attachment": "Ek indir",
+ "aria/Edit Message": "Mesajı Düzenle",
"aria/Emoji picker": "Emoji seçici",
"aria/File input": "Dosya girişi",
"aria/File upload": "Dosya yükleme",
+ "aria/Flag Message": "Mesajı bayrakla",
"aria/Image input": "Resim girişi",
"aria/Load More Channels": "Daha Fazla Kanal Yükle",
+ "aria/Mark Message Unread": "Okunmamış olarak işaretle",
"aria/Menu": "Menü",
"aria/Message Options": "Mesaj Seçenekleri",
+ "aria/Mute User": "Kullanıcıyı sustur",
"aria/Open Attachment Selector": "Ek Seçiciyi Aç",
"aria/Open Menu": "Menüyü Aç",
"aria/Open Message Actions Menu": "Mesaj İşlemleri Menüsünü Aç",
"aria/Open Reaction Selector": "Tepki Seçiciyi Aç",
"aria/Open Thread": "Konuyu Aç",
+ "aria/Pin Message": "Mesajı sabitle",
+ "aria/Quote Message": "Mesajı alıntıla",
"aria/Reaction list": "Tepki listesi",
+ "aria/Remind Me Message": "Hatırlat",
"aria/Remind Me Options": "Hatırlatma seçenekleri",
"aria/Remove attachment": "Eki kaldır",
"aria/Remove location attachment": "Konum ekini kaldır",
+ "aria/Resend Message": "Mesajı tekrar gönder",
"aria/Retry upload": "Yüklemeyi Tekrar Dene",
"aria/Search results": "Arama sonuçları",
"aria/Search results header filter button": "Arama sonuçları başlık filtre düğmesi",
"aria/Send": "Gönder",
"aria/Show preview": "Önizlemeyi göster",
"aria/Stop AI Generation": "Yapay Zeka Üretimini Durdur",
+ "aria/Unblock User": "Kullanıcının engelini kaldır",
+ "aria/Unmute User": "Sesini aç",
+ "aria/Unpin Message": "Sabitlemeyi kaldır",
"Ask a question": "Bir soru sor",
"Attach": "Ekle",
"Attach files": "Dosya ekle",
@@ -77,6 +92,7 @@
"Back": "Geri",
"ban-command-args": "[@kullanıcıadı] [metin]",
"ban-command-description": "Bir kullanıcıyı yasakla",
+ "Block User": "Kullanıcıyı engelle",
"Cancel": "İptal",
"Cannot seek in the recording": "Kayıtta arama yapılamıyor",
"Channel Missing": "Kanal bulunamıyor",
@@ -87,6 +103,7 @@
"Commands": "Komutlar",
"Commands matching": "Eşleşen komutlar",
"Connection failure, reconnecting now...": "Bağlantı hatası, tekrar bağlanılıyor...",
+ "Copy Message": "Mesajı kopyala",
"Create": "Oluştur",
"Create poll": "Anket oluştur",
"Current location": "Mevcut konum",
@@ -206,6 +223,7 @@
"Reply to Message": "Mesaja Cevapla",
"replyCount_one": "1 cevap",
"replyCount_other": "{{ count }} cevap",
+ "Resend": "Tekrar gönder",
"Retry upload": "Yüklemeyi yeniden dene",
"Save for later": "Daha sonra kaydet",
"Saved for later": "Daha sonra kaydedildi",
@@ -253,6 +271,7 @@
"Thread": "Konu",
"Thread has not been found": "Konu bulunamadı",
"Thread reply": "Konu yanıtı",
+ "Thread Reply": "Konu yanıtı",
"Threads": "İleti dizileri",
"timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}",
"timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}",
@@ -269,6 +288,7 @@
"Unarchive": "Arşivden çıkar",
"unban-command-args": "[@kullanıcıadı]",
"unban-command-description": "Bir kullanıcının yasağını kaldır",
+ "Unblock User": "Kullanıcının engelini kaldır",
"unknown error": "bilinmeyen hata",
"Unmute": "Sesini aç",
"unmute-command-args": "[@kullanıcıadı]",
From fd256155c7bfd2f8f4e103e2a643e247d34950dd Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 17:54:25 +0100
Subject: [PATCH 02/11] feat: add icons to default MessageActions menus
---
src/components/Channel/Channel.tsx | 10 ++++++-
.../AudioRecorder/styling/AudioRecorder.scss | 1 +
src/components/Message/MessageSimple.tsx | 20 +++++++++-----
src/components/Message/utils.tsx | 3 +++
.../MessageActions/RemindMeSubmenu.tsx | 3 ++-
src/components/MessageActions/defaults.tsx | 27 +++++++++++++------
.../hooks/useBaseMessageActionSetFilter.ts | 20 ++++++++++----
.../MessageInput/styling/MessageComposer.scss | 13 +++------
8 files changed, 66 insertions(+), 31 deletions(-)
diff --git a/src/components/Channel/Channel.tsx b/src/components/Channel/Channel.tsx
index 17c0008215..4117ccde11 100644
--- a/src/components/Channel/Channel.tsx
+++ b/src/components/Channel/Channel.tsx
@@ -967,15 +967,23 @@ const ChannelInner = (
};
const retrySendMessage = async (localMessage: LocalMessage) => {
+ /**
+ * If type is not checked, and we for example send message.type === 'error',
+ * then request fails with error: "message.type must be one of ['' regular system]".
+ * For now, we re-send any other type to prevent breaking behavior.
+ */
+
+ const type = localMessage.type === 'error' ? 'regular' : localMessage.type;
updateMessage({
...localMessage,
error: undefined,
status: 'sending',
+ type,
});
await doSendMessage({
localMessage,
- message: localMessageToNewMessagePayload(localMessage),
+ message: localMessageToNewMessagePayload({ ...localMessage, type }),
});
};
diff --git a/src/components/MediaRecorder/AudioRecorder/styling/AudioRecorder.scss b/src/components/MediaRecorder/AudioRecorder/styling/AudioRecorder.scss
index 9a58cc58e7..7f24d7cfb7 100644
--- a/src/components/MediaRecorder/AudioRecorder/styling/AudioRecorder.scss
+++ b/src/components/MediaRecorder/AudioRecorder/styling/AudioRecorder.scss
@@ -42,6 +42,7 @@
.str-chat__audio-recorder__recording-preview {
.str-chat__icon--microphone {
+ height: var(--icon-size-sm);
width: var(--icon-size-sm);
color: var(--button-destructive-text);
}
diff --git a/src/components/Message/MessageSimple.tsx b/src/components/Message/MessageSimple.tsx
index 950c3cbebd..71d8efc99e 100644
--- a/src/components/Message/MessageSimple.tsx
+++ b/src/components/Message/MessageSimple.tsx
@@ -21,6 +21,7 @@ import {
isMessageBlocked,
isMessageBounced,
isMessageEdited,
+ isMessageErrorRetryable,
isOnlyEmojis,
messageHasAttachments,
messageHasGiphyAttachment,
@@ -52,7 +53,7 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
groupedByUser,
handleAction,
handleOpenThread,
- handleRetry,
+ // handleRetry,
highlighted,
isMessageAIGenerated,
isMyMessage,
@@ -120,17 +121,22 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
const showReplyCountButton = !threadList && !!message.reply_count;
const showIsReplyInChannel =
!threadList && message.show_in_channel && message.parent_id;
- const allowRetry = message.status === 'failed' && message.error?.status !== 403;
+ const allowRetry = isMessageErrorRetryable(message);
const isBounced = isMessageBounced(message);
const isEdited = isMessageEdited(message) && !isAIGenerated;
let handleClick: (() => void) | undefined = undefined;
- if (allowRetry) {
- handleClick = () => handleRetry(message);
- } else if (isBounced) {
- handleClick = () => setIsBounceDialogOpen(true);
- } else if (isEdited) {
+ // todo: should we keep the behavior with click-on-blubble?
+ // if (allowRetry) {
+ // handleClick = () => handleRetry(message);
+ // } else if (isBounced) {
+ // handleClick = () => setIsBounceDialogOpen(true);
+ // } else if (isEdited) {
+ // handleClick = () => setEditedTimestampOpen((prev) => !prev);
+ // }
+
+ if (isEdited) {
handleClick = () => setEditedTimestampOpen((prev) => !prev);
}
diff --git a/src/components/Message/utils.tsx b/src/components/Message/utils.tsx
index 2faf3f04cf..2727845fe3 100644
--- a/src/components/Message/utils.tsx
+++ b/src/components/Message/utils.tsx
@@ -441,6 +441,9 @@ export const countEmojis = (text?: string) => {
return matches ? matches.length : 0;
};
+export const isMessageErrorRetryable = (message: LocalMessage) =>
+ message.status === 'failed' && message.error?.status !== 403;
+
export const isMessageBounced = (
message: Pick,
) =>
diff --git a/src/components/MessageActions/RemindMeSubmenu.tsx b/src/components/MessageActions/RemindMeSubmenu.tsx
index 9c9d1899ae..aab2876c47 100644
--- a/src/components/MessageActions/RemindMeSubmenu.tsx
+++ b/src/components/MessageActions/RemindMeSubmenu.tsx
@@ -7,7 +7,7 @@ import {
ContextMenuHeader,
useContextMenuContext,
} from '../Dialog';
-import { IconBellNotification, IconChevronLeft } from '../Icons';
+import { IconBellNotification, IconChevronLeft, IconPlusSmall } from '../Icons';
// todo: do we need to have isMine as a prop?
export type RemindMeActionButtonProps = { isMine: boolean } & BaseContextMenuButtonProps;
@@ -58,6 +58,7 @@ export const RemindMeSubmenu = () => {
{client.reminders.scheduledOffsetsMs.map((offsetMs) => (
{
client.reminders.upsertReminder({
diff --git a/src/components/MessageActions/defaults.tsx b/src/components/MessageActions/defaults.tsx
index 239a53c6c8..bb4abaaa75 100644
--- a/src/components/MessageActions/defaults.tsx
+++ b/src/components/MessageActions/defaults.tsx
@@ -22,7 +22,12 @@ import {
ThreadIcon,
} from '../../components/Message/icons';
import { ReactionSelectorWithButton } from '../../components/Reactions/ReactionSelectorWithButton';
-import { useChatContext, useMessageContext, useTranslationContext } from '../../context';
+import {
+ useChannelActionContext,
+ useChatContext,
+ useMessageContext,
+ useTranslationContext,
+} from '../../context';
import {
RemindMeActionButton,
RemindMeSubmenu,
@@ -132,8 +137,7 @@ const DefaultMessageActionComponents = {
);
},
Resend({ closeMenu }: ContextMenuItemProps) {
- const messageComposer = useMessageComposer();
- const { message } = useMessageContext();
+ const { handleRetry, message } = useMessageContext();
const { t } = useTranslationContext();
return (
@@ -143,7 +147,7 @@ const DefaultMessageActionComponents = {
className={msgActionsBoxButtonClassName}
Icon={IconArrowRotateClockwise}
onClick={() => {
- messageComposer.initState({ composition: message });
+ handleRetry(message);
closeMenu();
}}
>
@@ -276,7 +280,8 @@ const DefaultMessageActionComponents = {
);
},
Delete({ closeMenu }: ContextMenuItemProps) {
- const { handleDelete } = useMessageContext();
+ const { removeMessage } = useChannelActionContext();
+ const { handleDelete, message } = useMessageContext();
const { t } = useTranslationContext();
return (
@@ -289,7 +294,8 @@ const DefaultMessageActionComponents = {
)}
Icon={IconTrashBin}
onClick={(event) => {
- handleDelete(event);
+ if (message.type === 'error') removeMessage(message);
+ else handleDelete(event);
closeMenu();
}}
>
@@ -380,15 +386,20 @@ export const defaultMessageActionSet: MessageActionSetItem[] = [
type: 'copyMessageText',
},
{
- Component: DefaultMessageActionComponents.dropdown.MarkUnread,
+ Component: DefaultMessageActionComponents.dropdown.Resend,
placement: 'dropdown',
- type: 'markUnread',
+ type: 'resendMessage',
},
{
Component: DefaultMessageActionComponents.dropdown.Edit,
placement: 'dropdown',
type: 'edit',
},
+ {
+ Component: DefaultMessageActionComponents.dropdown.MarkUnread,
+ placement: 'dropdown',
+ type: 'markUnread',
+ },
{
Component: DefaultMessageActionComponents.dropdown.RemindMe,
placement: 'dropdown',
diff --git a/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts b/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts
index 1c83cb3313..7685382161 100644
--- a/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts
+++ b/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts
@@ -2,7 +2,11 @@ import { useMemo } from 'react';
import { useChannelStateContext, useMessageContext } from '../../../context';
import { useUserRole } from '../../Message/hooks';
-import { ACTIONS_NOT_WORKING_IN_THREAD } from '../../Message/utils';
+import {
+ ACTIONS_NOT_WORKING_IN_THREAD,
+ isMessageBounced,
+ isMessageErrorRetryable,
+} from '../../Message/utils';
import type { MessageActionSetItem } from '../MessageActions';
@@ -31,6 +35,8 @@ export const useBaseMessageActionSetFilter = (
canSendMessage,
} = useUserRole(message);
const isMessageThreadReply = typeof message.parent_id === 'string';
+ const isBounced = isMessageBounced(message);
+ const allowRetry = isMessageErrorRetryable(message);
return useMemo(() => {
if (disable) return messageActionSet;
@@ -51,15 +57,16 @@ export const useBaseMessageActionSetFilter = (
return false;
// failed message menu has special treatment
- if (message.status === 'failed' || message.type === 'error') {
+ if (isBounced || message.error) {
return (
- (type === 'resendMessage' && !canSendMessage) ||
- (type === 'edit' && !canEdit) ||
- (type === 'delete' && !canDelete)
+ (type === 'resendMessage' && canSendMessage && (allowRetry || isBounced)) ||
+ (type === 'edit' && canEdit && isBounced) ||
+ (type === 'delete' && canDelete && isBounced)
);
}
if (
+ type === 'resendMessage' ||
(type === 'blockUser' && !canBlockUser) ||
(type === 'copyMessageText' && !message.text) ||
(type === 'delete' && !canDelete) ||
@@ -78,6 +85,7 @@ export const useBaseMessageActionSetFilter = (
return true;
});
}, [
+ allowRetry,
canBlockUser,
canDelete,
canEdit,
@@ -89,8 +97,10 @@ export const useBaseMessageActionSetFilter = (
canReply,
canSendMessage,
channelConfig,
+ isBounced,
isInitialMessage,
isMessageThreadReply,
+ message.error,
message.status,
message.text,
message.type,
diff --git a/src/components/MessageInput/styling/MessageComposer.scss b/src/components/MessageInput/styling/MessageComposer.scss
index 65c8612d88..d14d56879f 100644
--- a/src/components/MessageInput/styling/MessageComposer.scss
+++ b/src/components/MessageInput/styling/MessageComposer.scss
@@ -115,27 +115,22 @@
display: flex;
cursor: pointer;
- .str-chat__icon--emoji {
+ svg {
+ height: var(--icon-size-md);
width: var(--icon-size-md);
}
}
.str-chat__start-recording-audio-button {
.str-chat__icon--microphone {
+ height: var(--icon-size-md);
width: var(--icon-size-md);
}
}
.str-chat__send-button {
.str-chat__icon--paper-plane {
- fill: none;
-
- path {
- color: var(--button-primary-text-on-accent);
- }
- }
-
- .str-chat__icon--paper-plane {
+ height: var(--icon-size-md);
width: var(--icon-size-md);
}
}
From bd652c02cf145c7c0334320c1f8e32e910a940f8 Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 17:55:08 +0100
Subject: [PATCH 03/11] fix: correct the condition to decide, whether poll
actions should be shown
---
src/components/Poll/PollActions/PollActions.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Poll/PollActions/PollActions.tsx b/src/components/Poll/PollActions/PollActions.tsx
index f7b134843c..b63072ff3e 100644
--- a/src/components/Poll/PollActions/PollActions.tsx
+++ b/src/components/Poll/PollActions/PollActions.tsx
@@ -88,7 +88,7 @@ export const PollActions = ({
(!is_closed && created_by_id === client.user?.id) ||
!!voteCount ||
options.length > MAX_OPTIONS_DISPLAYED ||
- canCastVote ||
+ (canCastVote && allow_user_suggested_options && options.length < MAX_POLL_OPTIONS) ||
(!is_closed && allow_answers) ||
(answers_count > 0 && channelCapabilities['query-poll-votes']);
From 455e0a5bc31e0c462af408d6d34549345d2aae11 Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 17:55:35 +0100
Subject: [PATCH 04/11] refactor: remove unnecessary Emoji plugin styling
---
examples/vite/src/index.scss | 1 -
package.json | 2 +-
src/plugins/Emojis/styling/icons.scss | 7 -------
src/plugins/Emojis/styling/index.scss | 1 -
4 files changed, 1 insertion(+), 10 deletions(-)
delete mode 100644 src/plugins/Emojis/styling/icons.scss
delete mode 100644 src/plugins/Emojis/styling/index.scss
diff --git a/examples/vite/src/index.scss b/examples/vite/src/index.scss
index 54a8f367ac..be264710e9 100644
--- a/examples/vite/src/index.scss
+++ b/examples/vite/src/index.scss
@@ -5,7 +5,6 @@
// v3 CSS import
@import url('stream-chat-react/dist/css/index.css') layer(stream-new);
-@import url('stream-chat-react/dist/css/emojis.css') layer(stream-new);
@import url('./AppSettings/AppSettings.scss') layer(stream-app-overrides);
:root {
diff --git a/package.json b/package.json
index 23396c7251..d63ec1ca67 100644
--- a/package.json
+++ b/package.json
@@ -221,7 +221,7 @@
"scripts": {
"clean": "rm -rf dist",
"build": "yarn clean && concurrently './scripts/copy-css.sh' 'yarn build-translations' 'vite build' 'tsc --project tsconfig.lib.json' 'yarn build-styling'",
- "build-styling": "sass src/styling/index.scss dist/css/index.css && sass src/plugins/Emojis/styling/index.scss dist/css/emojis.css",
+ "build-styling": "sass src/styling/index.scss dist/css/index.css",
"build-translations": "i18next-cli extract",
"coverage": "jest --collectCoverage && codecov",
"lint": "yarn prettier --list-different && yarn eslint && yarn validate-translations",
diff --git a/src/plugins/Emojis/styling/icons.scss b/src/plugins/Emojis/styling/icons.scss
deleted file mode 100644
index 91df62eb1a..0000000000
--- a/src/plugins/Emojis/styling/icons.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.str-chat {
- .str-chat__icon--emoji {
- path {
- stroke-width: 1.2;
- }
- }
-}
diff --git a/src/plugins/Emojis/styling/index.scss b/src/plugins/Emojis/styling/index.scss
deleted file mode 100644
index eba2d4d73d..0000000000
--- a/src/plugins/Emojis/styling/index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@use 'icons';
From 35c03143335338e270133daba6679751f58b4c68 Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 18:09:15 +0100
Subject: [PATCH 05/11] feat: position the message error icon at the beginning
of the top right border radius
---
src/components/Message/styling/Message.scss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Message/styling/Message.scss b/src/components/Message/styling/Message.scss
index c2cd0a1934..dbfaa09bd5 100644
--- a/src/components/Message/styling/Message.scss
+++ b/src/components/Message/styling/Message.scss
@@ -486,7 +486,7 @@
.str-chat__message-error-icon {
display: block;
position: absolute;
- bottom: 0;
+ top: 8px;
inset-inline-end: calc(-1 * calc(#{$icon-size} / 2));
svg {
From 8b121579f73d74a273d05c9120f62da710e5def9 Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 18:11:55 +0100
Subject: [PATCH 06/11] feat: track aria-selected in ContextMenuButton
---
.../Dialog/base/ContextMenuButton.tsx | 24 ++++++++++++++++---
.../MessageActions/RemindMeSubmenu.tsx | 7 +-----
src/components/MessageActions/defaults.tsx | 13 ----------
3 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/src/components/Dialog/base/ContextMenuButton.tsx b/src/components/Dialog/base/ContextMenuButton.tsx
index 3f8a287390..5aebdbca58 100644
--- a/src/components/Dialog/base/ContextMenuButton.tsx
+++ b/src/components/Dialog/base/ContextMenuButton.tsx
@@ -234,6 +234,24 @@ const ContextMenuButtonWithSubmenu = ({
type ContextMenuButtonProps = BaseContextMenuButtonProps;
-export const ContextMenuButton = (props: ContextMenuButtonProps) => (
-
-);
+export const ContextMenuButton = ({
+ onBlur,
+ onFocus,
+ ...props
+}: ContextMenuButtonProps) => {
+ const [isFocused, setIsFocused] = useState(false);
+ return (
+ {
+ setIsFocused(false);
+ onBlur?.(e);
+ }}
+ onFocus={(e) => {
+ setIsFocused(true);
+ onFocus?.(e);
+ }}
+ />
+ );
+};
diff --git a/src/components/MessageActions/RemindMeSubmenu.tsx b/src/components/MessageActions/RemindMeSubmenu.tsx
index aab2876c47..f7c5d3716c 100644
--- a/src/components/MessageActions/RemindMeSubmenu.tsx
+++ b/src/components/MessageActions/RemindMeSubmenu.tsx
@@ -20,12 +20,7 @@ export const RemindMeActionButton = ({
const { t } = useTranslationContext();
return (
-
+
{t('Remind Me')}
);
diff --git a/src/components/MessageActions/defaults.tsx b/src/components/MessageActions/defaults.tsx
index bb4abaaa75..210c991e9a 100644
--- a/src/components/MessageActions/defaults.tsx
+++ b/src/components/MessageActions/defaults.tsx
@@ -53,7 +53,6 @@ const DefaultMessageActionComponents = {
return (
{
handleQuote();
@@ -105,7 +103,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -124,7 +121,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -143,7 +139,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -163,7 +158,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -182,7 +176,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -201,7 +194,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -246,7 +237,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -267,7 +257,6 @@ const DefaultMessageActionComponents = {
return (
{
@@ -287,7 +276,6 @@ const DefaultMessageActionComponents = {
return (
Date: Tue, 17 Feb 2026 18:12:35 +0100
Subject: [PATCH 07/11] style: fix linter issues
---
src/components/Form/styling/Form.scss | 2 +-
src/components/Form/styling/NumericInput.scss | 4 +-
src/components/Form/styling/SwitchField.scss | 25 ++++++---
.../Form/styling/TextInputFieldset.scss | 2 +-
src/components/Icons/styling/Icons.scss | 2 +-
src/components/Icons/styling/index.scss | 2 +-
src/components/Modal/styling/Modal.scss | 2 +-
.../Poll/styling/PollCreationDialog.scss | 6 ++-
src/components/Poll/styling/PollResults.scss | 1 -
src/styling/_global-theme-variables.scss | 51 ++++++++-----------
10 files changed, 51 insertions(+), 46 deletions(-)
diff --git a/src/components/Form/styling/Form.scss b/src/components/Form/styling/Form.scss
index cf5d78bdc0..b62dfcb990 100644
--- a/src/components/Form/styling/Form.scss
+++ b/src/components/Form/styling/Form.scss
@@ -21,4 +21,4 @@ input[type='number'] {
.str-chat__form-field-error {
margin-left: 0.5rem;
}
-}
\ No newline at end of file
+}
diff --git a/src/components/Form/styling/NumericInput.scss b/src/components/Form/styling/NumericInput.scss
index 04cc55caa3..99db30069d 100644
--- a/src/components/Form/styling/NumericInput.scss
+++ b/src/components/Form/styling/NumericInput.scss
@@ -40,7 +40,9 @@
border-radius: var(--button-radius-full, 9999px);
color: var(--text-tertiary, #687385);
cursor: pointer;
- transition: border-color 0.15s ease, color 0.15s ease;
+ transition:
+ border-color 0.15s ease,
+ color 0.15s ease;
&:hover:not(:disabled) {
color: var(--text-primary, #1a1b25);
diff --git a/src/components/Form/styling/SwitchField.scss b/src/components/Form/styling/SwitchField.scss
index 3e464b93ae..f2617a8171 100644
--- a/src/components/Form/styling/SwitchField.scss
+++ b/src/components/Form/styling/SwitchField.scss
@@ -3,17 +3,29 @@
// CSS variables aligned with Figma tokens; fallbacks from get_variable_defs.
.str-chat {
- --str-chat__switch-field-background-color: var(--input-cards-bg, var(--background-core-surface-subtle));
+ --str-chat__switch-field-background-color: var(
+ --input-cards-bg,
+ var(--background-core-surface-subtle)
+ );
--str-chat__switch-field-border-radius: var(--radius-md);
--str-chat__switch-field-title-font-size: var(--typography-font-size-sm, 14px);
--str-chat__switch-field-title-font-weight: var(--typography-font-weight-medium, 500);
--str-chat__switch-field-title-line-height: var(--typography-line-height-tight, 16px);
--str-chat__switch-field-title-color: var(--text-primary, #1a1b25);
--str-chat__switch-field-description-font-size: var(--typography-font-size-xs, 12px);
- --str-chat__switch-field-description-font-weight: var(--typography-font-weight-regular, 400);
+ --str-chat__switch-field-description-font-weight: var(
+ --typography-font-weight-regular,
+ 400
+ );
--str-chat__switch-field-description-color: var(--text-tertiary, #687385);
- --str-chat__switch-field__track-off-bg: var(--control-toggle-switch-bg, var(--border-core-on-surface, #a3acba));
- --str-chat__switch-field__track-on-bg: var(--control-toggle-switch-bg-selected, #005fff);
+ --str-chat__switch-field__track-off-bg: var(
+ --control-toggle-switch-bg,
+ var(--border-core-on-surface, #a3acba)
+ );
+ --str-chat__switch-field__track-on-bg: var(
+ --control-toggle-switch-bg-selected,
+ #005fff
+ );
--str-chat__switch-field__track-thumb-bg: var(--base-white, #ffffff);
--str-chat__switch-field__track-height: 24px;
--str-chat__switch-field__track-radius: var(--button-radius-full, 9999px);
@@ -25,12 +37,11 @@
}
.str-chat__form__switch-field {
-
display: flex;
align-items: center;
gap: var(--spacing-sm);
width: 100%;
- padding: var(--spacing-sm ) var(--spacing-md);
+ padding: var(--spacing-sm) var(--spacing-md);
background-color: var(--str-chat__switch-field-background-color);
border-radius: var(--str-chat__switch-field-border-radius);
box-sizing: border-box;
@@ -137,7 +148,7 @@
.str-chat__form__switch-field__label,
.str-chat__form__switch-field__label__content {
- flex: 1
+ flex: 1;
}
.str-chat__form__switch-field__label--as-error {
diff --git a/src/components/Form/styling/TextInputFieldset.scss b/src/components/Form/styling/TextInputFieldset.scss
index 5c84534e69..1490d85590 100644
--- a/src/components/Form/styling/TextInputFieldset.scss
+++ b/src/components/Form/styling/TextInputFieldset.scss
@@ -42,4 +42,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/components/Icons/styling/Icons.scss b/src/components/Icons/styling/Icons.scss
index b28f54b480..5a3d4a4905 100644
--- a/src/components/Icons/styling/Icons.scss
+++ b/src/components/Icons/styling/Icons.scss
@@ -3,4 +3,4 @@
width: 1em;
height: 1em;
fill: currentColor;
-}
\ No newline at end of file
+}
diff --git a/src/components/Icons/styling/index.scss b/src/components/Icons/styling/index.scss
index 4089cfd734..c2efae148e 100644
--- a/src/components/Icons/styling/index.scss
+++ b/src/components/Icons/styling/index.scss
@@ -1 +1 @@
-@use 'Icons';
\ No newline at end of file
+@use 'Icons';
diff --git a/src/components/Modal/styling/Modal.scss b/src/components/Modal/styling/Modal.scss
index bc0965f977..f6a1ca679f 100644
--- a/src/components/Modal/styling/Modal.scss
+++ b/src/components/Modal/styling/Modal.scss
@@ -92,4 +92,4 @@
width: 12px;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/components/Poll/styling/PollCreationDialog.scss b/src/components/Poll/styling/PollCreationDialog.scss
index 007e2cd05d..fbda78f2db 100644
--- a/src/components/Poll/styling/PollCreationDialog.scss
+++ b/src/components/Poll/styling/PollCreationDialog.scss
@@ -24,11 +24,13 @@
}
.str-chat__form__input-field__value input,
- .str-chat__form__input-field__value.str-chat__form-text-input .str-chat__form-text-input__wrapper {
+ .str-chat__form__input-field__value.str-chat__form-text-input
+ .str-chat__form-text-input__wrapper {
width: 100%;
}
- .str-chat__form__input-field__value.str-chat__form-text-input .str-chat__form-text-input__wrapper {
+ .str-chat__form__input-field__value.str-chat__form-text-input
+ .str-chat__form-text-input__wrapper {
background-color: transparent;
}
diff --git a/src/components/Poll/styling/PollResults.scss b/src/components/Poll/styling/PollResults.scss
index c686bc02b2..9d8ea927de 100644
--- a/src/components/Poll/styling/PollResults.scss
+++ b/src/components/Poll/styling/PollResults.scss
@@ -19,4 +19,3 @@
padding-inline: 1rem;
}
}
-
diff --git a/src/styling/_global-theme-variables.scss b/src/styling/_global-theme-variables.scss
index 860f0a9ac1..ea16358481 100644
--- a/src/styling/_global-theme-variables.scss
+++ b/src/styling/_global-theme-variables.scss
@@ -37,36 +37,25 @@
var(--typography-font-family-sans), system-ui, -apple-system, BlinkMacSystemFont,
Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif;
- --str-chat__metadata-default-text: normal
- var(--typography-font-weight-regular)
- var(--typography-font-size-xs) /
- var(--typography-line-height-tight)
- var(--str-chat__font-family);
-
- --str-chat__caption-emphasis-text: normal
- var(--typography-font-weight-semi-bold)
- var(--typography-font-size-sm) /
- var(--typography-line-height-tight)
- var(--str-chat__font-family);
-
- --str-chat__body-emphasis-text: normal
- var(--typography-font-weight-semi-bold)
- var(--typography-font-size-md) /
- var(--typography-line-height-normal)
- var(--str-chat__font-family);
-
- --str-chat__heading-xs-text: normal
- var(--typography-font-weight-medium)
- var(--typography-font-size-sm) /
- var(--typography-line-height-tight)
- var(--str-chat__font-family);
-
- --str-chat__heading-sm-text: normal
- var(--typography-font-weight-semi-bold)
- var(--typography-font-size-md) /
- var(--typography-line-height-normal)
- var(--str-chat__font-family);
+ --str-chat__metadata-default-text: normal var(--typography-font-weight-regular)
+ var(--typography-font-size-xs) / var(--typography-line-height-tight)
+ var(--str-chat__font-family);
+ --str-chat__caption-emphasis-text: normal var(--typography-font-weight-semi-bold)
+ var(--typography-font-size-sm) / var(--typography-line-height-tight)
+ var(--str-chat__font-family);
+
+ --str-chat__body-emphasis-text: normal var(--typography-font-weight-semi-bold)
+ var(--typography-font-size-md) / var(--typography-line-height-normal)
+ var(--str-chat__font-family);
+
+ --str-chat__heading-xs-text: normal var(--typography-font-weight-medium)
+ var(--typography-font-size-sm) / var(--typography-line-height-tight)
+ var(--str-chat__font-family);
+
+ --str-chat__heading-sm-text: normal var(--typography-font-weight-semi-bold)
+ var(--typography-font-size-md) / var(--typography-line-height-normal)
+ var(--str-chat__font-family);
// todo: adapt the old text variables to so that they use the new semantic text variables
/* The font used for caption texts */
@@ -189,7 +178,9 @@
/* If a component has a box shadow applied to it, this will be the color used for the shadow */
--str-chat__box-shadow-color: rgba(0, 0, 0, 0.18);
- --str-chat__box-shadow-elevation-1: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 6px 12px 0 rgba(0, 0, 0, 0.16), 0 20px 32px 0 rgba(0, 0, 0, 0.12);
+ --str-chat__box-shadow-elevation-1:
+ 0 0 0 1px rgba(0, 0, 0, 0.05), 0 6px 12px 0 rgba(0, 0, 0, 0.16),
+ 0 20px 32px 0 rgba(0, 0, 0, 0.12);
/* Used for online indicator and success messages */
--str-chat__info-color: var(--str-chat__green500);
From 74a45a7d162713c3e9a0e2ca2517b4ae071af9f2 Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 18:17:03 +0100
Subject: [PATCH 08/11] feat: remove the resend-on-message-click behavior
---
src/components/Message/MessageSimple.tsx | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/src/components/Message/MessageSimple.tsx b/src/components/Message/MessageSimple.tsx
index 71d8efc99e..43c17790aa 100644
--- a/src/components/Message/MessageSimple.tsx
+++ b/src/components/Message/MessageSimple.tsx
@@ -53,7 +53,6 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
groupedByUser,
handleAction,
handleOpenThread,
- // handleRetry,
highlighted,
isMessageAIGenerated,
isMyMessage,
@@ -127,16 +126,10 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
let handleClick: (() => void) | undefined = undefined;
- // todo: should we keep the behavior with click-on-blubble?
- // if (allowRetry) {
- // handleClick = () => handleRetry(message);
- // } else if (isBounced) {
- // handleClick = () => setIsBounceDialogOpen(true);
- // } else if (isEdited) {
- // handleClick = () => setEditedTimestampOpen((prev) => !prev);
- // }
-
- if (isEdited) {
+ // todo: should we keep the behavior with click-on-blubble -> show the MessageBounceModal?
+ if (isBounced) {
+ handleClick = () => setIsBounceDialogOpen(true);
+ } else if (isEdited) {
handleClick = () => setEditedTimestampOpen((prev) => !prev);
}
From 25150c237adb86f8e6d3f07a6d2a81aab56289f6 Mon Sep 17 00:00:00 2001
From: martincupela
Date: Tue, 17 Feb 2026 19:25:47 +0100
Subject: [PATCH 09/11] feat: integrate DialogAnchor into ContextMenu to keep
stable position to reference element
---
src/components/Dialog/base/ContextMenu.tsx | 88 ++++++++++++++++++-
.../Dialog/service/DialogAnchor.tsx | 22 +++--
.../MessageActions/MessageActions.tsx | 22 ++---
.../AttachmentSelector/AttachmentSelector.tsx | 21 ++---
4 files changed, 117 insertions(+), 36 deletions(-)
diff --git a/src/components/Dialog/base/ContextMenu.tsx b/src/components/Dialog/base/ContextMenu.tsx
index a05fc776ec..b7e551a404 100644
--- a/src/components/Dialog/base/ContextMenu.tsx
+++ b/src/components/Dialog/base/ContextMenu.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
import React, {
type ComponentProps,
type ComponentType,
@@ -10,6 +11,9 @@ import React, {
} from 'react';
import clsx from 'clsx';
import { IconChevronLeft } from '../../Icons';
+import { useDialogIsOpen } from '../hooks';
+import type { DialogAnchorProps } from '../service/DialogAnchor';
+import { DialogAnchor } from '../service/DialogAnchor';
export const ContextMenuBackButton = ({
children,
@@ -96,7 +100,7 @@ type ContextMenuLevel = {
menuClassName?: string;
};
-export type ContextMenuProps = Omit, 'children'> & {
+type ContextMenuBaseProps = Omit, 'children'> & {
backLabel?: ReactNode;
items: ContextMenuItemComponent[];
Header?: ContextMenuHeaderComponent;
@@ -106,7 +110,24 @@ export type ContextMenuProps = Omit, 'children'> & {
onMenuLevelChange?: (level: number) => void;
};
-export const ContextMenu = ({
+/** When provided, ContextMenu renders inside DialogAnchor and wires menu level for submenu alignment. */
+type ContextMenuAnchorProps = Partial<
+ Pick<
+ DialogAnchorProps,
+ | 'id'
+ | 'dialogManagerId'
+ | 'placement'
+ | 'referenceElement'
+ | 'tabIndex'
+ | 'trapFocus'
+ | 'allowFlip'
+ | 'focus'
+ >
+>;
+
+export type ContextMenuProps = ContextMenuBaseProps & ContextMenuAnchorProps;
+
+function ContextMenuContent({
backLabel = 'Back',
className,
Header,
@@ -116,7 +137,7 @@ export const ContextMenu = ({
onClose,
onMenuLevelChange,
...props
-}: ContextMenuProps) => {
+}: ContextMenuBaseProps) {
const rootLevel = useMemo(
() => ({
Header,
@@ -207,4 +228,65 @@ export const ContextMenu = ({
);
+}
+
+export const ContextMenu = (props: ContextMenuProps) => {
+ const {
+ allowFlip,
+ dialogManagerId,
+ focus,
+ id,
+ placement,
+ referenceElement,
+ tabIndex,
+ trapFocus,
+ ...menuProps
+ } = props;
+
+ const isAnchored = id != null;
+
+ const [menuLevel, setMenuLevel] = useState(1);
+ const open = useDialogIsOpen(id ?? '', dialogManagerId);
+
+ useEffect(() => {
+ if (isAnchored && !open) setMenuLevel(1);
+ }, [isAnchored, open]);
+
+ const content = (
+
+ );
+
+ if (isAnchored) {
+ const {
+ backLabel: _b,
+ Header: _h,
+ items: _i,
+ ItemsWrapper: _w,
+ menuClassName: _m,
+ onClose: _c,
+ onMenuLevelChange: _l,
+ ...anchorDivProps
+ } = menuProps;
+ return (
+
+ {content}
+
+ );
+ }
+
+ return content;
};
diff --git a/src/components/Dialog/service/DialogAnchor.tsx b/src/components/Dialog/service/DialogAnchor.tsx
index a7213c5cb3..dadfc14088 100644
--- a/src/components/Dialog/service/DialogAnchor.tsx
+++ b/src/components/Dialog/service/DialogAnchor.tsx
@@ -1,6 +1,6 @@
import clsx from 'clsx';
import type { ComponentProps, PropsWithChildren } from 'react';
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
import { FocusScope } from '@react-aria/focus';
import { DialogPortalEntry } from './DialogPortal';
import { useDialog, useDialogIsOpen } from '../hooks';
@@ -29,22 +29,33 @@ export function useDialogAnchor({
placement,
});
+ // Freeze reference when dialog opens so submenus (e.g. ContextMenu level 2+) stay aligned to the original anchor
+ const frozenReferenceRef = useRef(null);
+ if (open && referenceElement && !frozenReferenceRef.current) {
+ frozenReferenceRef.current = referenceElement;
+ }
+ if (!open) {
+ frozenReferenceRef.current = null;
+ }
+ const effectiveReference = open ? frozenReferenceRef.current : referenceElement;
+
useEffect(() => {
- refs.setReference(referenceElement);
- }, [referenceElement, refs]);
+ refs.setReference(effectiveReference);
+ }, [effectiveReference, refs]);
useEffect(() => {
refs.setFloating(popperElement);
}, [popperElement, refs]);
useEffect(() => {
- if (open && popperElement) {
+ if (open && popperElement && effectiveReference) {
+ // Re-run when reference becomes available (e.g. after ref is set) or when updateKey changes (e.g. submenu open)
// Since the popper's reference element might not be (and usually is not) visible
// all the time, it's safer to force popper update before showing it.
// update is non-null only if popperElement is non-null
update?.();
}
- }, [open, placement, popperElement, update, updateKey]);
+ }, [open, placement, popperElement, update, updateKey, effectiveReference]);
if (popperElement && !open) {
setPopperElement(null);
@@ -83,6 +94,7 @@ export const DialogAnchor = ({
}: DialogAnchorProps) => {
const dialog = useDialog({ dialogManagerId, id });
const open = useDialogIsOpen(id, dialogManagerId);
+
const { setPopperElement, styles } = useDialogAnchor({
allowFlip,
open,
diff --git a/src/components/MessageActions/MessageActions.tsx b/src/components/MessageActions/MessageActions.tsx
index cff1ffc5f2..dab95c2ffd 100644
--- a/src/components/MessageActions/MessageActions.tsx
+++ b/src/components/MessageActions/MessageActions.tsx
@@ -6,7 +6,6 @@ import {
ContextMenu,
type ContextMenuItemComponent,
type ContextMenuItemProps,
- DialogAnchor,
useDialogIsOpen,
useDialogOnNearestManager,
} from '../Dialog';
@@ -117,25 +116,20 @@ export const MessageActions = ({
-
-
-
+ />
>
)}
{quickActionSet.map(({ Component: QuickActionComponent, type }) => (
diff --git a/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx b/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx
index 1b76095b76..223acc11fc 100644
--- a/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx
+++ b/src/components/MessageInput/AttachmentSelector/AttachmentSelector.tsx
@@ -17,7 +17,6 @@ import {
type ContextMenuItemProps,
type ContextMenuOpenSubmenuParams,
type ContextMenuSubmenu,
- DialogAnchor,
useDialogIsOpen,
useDialogOnNearestManager,
} from '../../Dialog';
@@ -329,7 +328,6 @@ export const AttachmentSelector = ({
const closeModal = useCallback(() => setModalContentActionAction(undefined), []);
const [fileInput, setFileInput] = useState(null);
- const [menuLevel, setMenuLevel] = useState(1);
const menuButtonRef = useRef(null);
const contextMenuItems = useMemo(
@@ -378,25 +376,20 @@ export const AttachmentSelector = ({
onClick={() => menuDialog?.toggle()}
ref={menuButtonRef}
/>
-
+ />
Date: Sun, 22 Feb 2026 07:12:05 +0100
Subject: [PATCH 10/11] feat(EmptyStateIndicator): apply Figma design to
str-chat__empty-channel
- Add EmptyStateIndicator/styling with Figma styles (tertiary color, caption typography, 12px gap)
- Update copy to 'Send a message to start the conversation' and i18n (all locales)
- Register styling in index.scss; leave str-chat__channel-list-empty unchanged
Co-authored-by: Cursor
---
.../EmptyStateIndicator.tsx | 2 +-
.../__tests__/EmptyStateIndicator.test.js | 4 ++-
.../styling/EmptyStateIndicator.scss | 33 +++++++++++++++++++
.../EmptyStateIndicator/styling/index.scss | 1 +
.../VirtualizedMessageList.test.js.snap | 2 +-
...tualizedMessageListComponents.test.js.snap | 2 +-
src/i18n/de.json | 1 +
src/i18n/en.json | 1 +
src/i18n/es.json | 1 +
src/i18n/fr.json | 1 +
src/i18n/hi.json | 1 +
src/i18n/it.json | 1 +
src/i18n/ja.json | 1 +
src/i18n/ko.json | 1 +
src/i18n/nl.json | 1 +
src/i18n/pt.json | 1 +
src/i18n/ru.json | 1 +
src/i18n/tr.json | 1 +
src/styling/index.scss | 1 +
19 files changed, 53 insertions(+), 4 deletions(-)
create mode 100644 src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss
create mode 100644 src/components/EmptyStateIndicator/styling/index.scss
diff --git a/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx b/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx
index f36d97ba45..881cb1240a 100644
--- a/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx
+++ b/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx
@@ -28,7 +28,7 @@ const UnMemoizedEmptyStateIndicator = (props: EmptyStateIndicatorProps) => {
}
if (listType === 'message') {
- const text = t('No chats here yet…');
+ const text = t('Send a message to start the conversation');
return (
diff --git a/src/components/EmptyStateIndicator/__tests__/EmptyStateIndicator.test.js b/src/components/EmptyStateIndicator/__tests__/EmptyStateIndicator.test.js
index 431298745b..58bde27d9a 100644
--- a/src/components/EmptyStateIndicator/__tests__/EmptyStateIndicator.test.js
+++ b/src/components/EmptyStateIndicator/__tests__/EmptyStateIndicator.test.js
@@ -22,7 +22,9 @@ describe('EmptyStateIndicator', () => {
it('should display correct text when listType is message', () => {
render();
- expect(screen.queryByText('No chats here yet…')).toBeInTheDocument();
+ expect(
+ screen.queryByText('Send a message to start the conversation'),
+ ).toBeInTheDocument();
});
it('should display correct text when listType is channel', () => {
diff --git a/src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss b/src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss
new file mode 100644
index 0000000000..f2b3f57573
--- /dev/null
+++ b/src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss
@@ -0,0 +1,33 @@
+// Figma design: empty channel only (str-chat__empty-channel). str-chat__channel-list-empty unchanged.
+.str-chat__empty-channel {
+ --str-chat-icon-color: var(--text-tertiary);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ padding: var(--str-chat__spacing-4);
+ position: relative;
+ gap: var(--str-chat__spacing-3); // 12px – Figma spacing/sm
+ text-align: center;
+
+ svg {
+ width: calc(var(--str-chat__spacing-px) * 136);
+ height: calc(var(--str-chat__spacing-px) * 136);
+
+ path {
+ fill: var(--text-tertiary);
+ }
+ }
+
+ .str-chat__empty-channel-notifications {
+ position: absolute;
+ inset-block-end: var(--str-chat__spacing-2);
+ }
+
+ .str-chat__empty-channel-text {
+ font: 400 14px / 20px var(--str-chat__font-family); // Figma caption/default
+ color: var(--text-tertiary);
+ margin: 0;
+ }
+}
diff --git a/src/components/EmptyStateIndicator/styling/index.scss b/src/components/EmptyStateIndicator/styling/index.scss
new file mode 100644
index 0000000000..d944802cf4
--- /dev/null
+++ b/src/components/EmptyStateIndicator/styling/index.scss
@@ -0,0 +1 @@
+@use 'EmptyStateIndicator';
diff --git a/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageList.test.js.snap b/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageList.test.js.snap
index 1a4d1ff5f4..7c2d48664f 100644
--- a/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageList.test.js.snap
+++ b/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageList.test.js.snap
@@ -47,7 +47,7 @@ exports[`VirtualizedMessageList should render the list without any message 1`] =
class="str-chat__empty-channel-text"
role="listitem"
>
- No chats here yet…
+ Send a message to start the conversation
diff --git a/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageListComponents.test.js.snap b/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageListComponents.test.js.snap
index 2054e88746..caf033d3f6 100644
--- a/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageListComponents.test.js.snap
+++ b/src/components/MessageList/__tests__/__snapshots__/VirtualizedMessageListComponents.test.js.snap
@@ -42,7 +42,7 @@ exports[`VirtualizedMessageComponents EmptyPlaceholder should render for main me
class="str-chat__empty-channel-text"
role="listitem"
>
- No chats here yet…
+ Send a message to start the conversation
diff --git a/src/i18n/de.json b/src/i18n/de.json
index 2641fec627..7ee2b764e3 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -244,6 +244,7 @@
"Select up to {{count}}_one": "Bis zu {{count}} auswählen",
"Select up to {{count}}_other": "Bis zu {{count}} auswählen",
"Send": "Senden",
+ "Send a message to start the conversation": "Sende eine Nachricht, um die Unterhaltung zu starten",
"Send Anyway": "Trotzdem senden",
"Send message request failed": "Senden der Nachrichtenanfrage fehlgeschlagen",
"Send poll": "Umfrage senden",
diff --git a/src/i18n/en.json b/src/i18n/en.json
index f6ece6b44f..2ca79e3e38 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -244,6 +244,7 @@
"Select up to {{count}}_one": "Select up to {{count}}",
"Select up to {{count}}_other": "Select up to {{count}}",
"Send": "Send",
+ "Send a message to start the conversation": "Send a message to start the conversation",
"Send Anyway": "Send Anyway",
"Send message request failed": "Send message request failed",
"Send poll": "Send poll",
diff --git a/src/i18n/es.json b/src/i18n/es.json
index 6efc585ba0..0440e82ca3 100644
--- a/src/i18n/es.json
+++ b/src/i18n/es.json
@@ -253,6 +253,7 @@
"Select up to {{count}}_many": "Selecciona hasta {{count}}",
"Select up to {{count}}_other": "Selecciona hasta {{count}}",
"Send": "Enviar",
+ "Send a message to start the conversation": "Envía un mensaje para iniciar la conversación",
"Send Anyway": "Enviar de todos modos",
"Send message request failed": "Error al enviar la solicitud de mensaje",
"Send poll": "Enviar encuesta",
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index fc3169a512..8f09df76e8 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -253,6 +253,7 @@
"Select up to {{count}}_many": "Sélectionner jusqu'à {{count}}",
"Select up to {{count}}_other": "Sélectionner jusqu'à {{count}}",
"Send": "Envoyer",
+ "Send a message to start the conversation": "Envoyez un message pour commencer la conversation",
"Send Anyway": "Envoyer quand même",
"Send message request failed": "Échec de la demande d'envoi de message",
"Send poll": "Envoyer le sondage",
diff --git a/src/i18n/hi.json b/src/i18n/hi.json
index 5ec287262d..66d4de15b2 100644
--- a/src/i18n/hi.json
+++ b/src/i18n/hi.json
@@ -245,6 +245,7 @@
"Select up to {{count}}_one": "अधिकतम {{count}} तक चुनें",
"Select up to {{count}}_other": "अधिकतम {{count}} तक चुनें",
"Send": "भेजे",
+ "Send a message to start the conversation": "बातचीत शुरू करने के लिए संदेश भेजें",
"Send Anyway": "वैसे भी भेजें",
"Send message request failed": "संदेश भेजने का अनुरोध विफल रहा",
"Send poll": "पोल भेजें",
diff --git a/src/i18n/it.json b/src/i18n/it.json
index 29d2a8dbdc..b8e1533821 100644
--- a/src/i18n/it.json
+++ b/src/i18n/it.json
@@ -253,6 +253,7 @@
"Select up to {{count}}_many": "Seleziona fino a {{count}}",
"Select up to {{count}}_other": "Seleziona fino a {{count}}",
"Send": "Invia",
+ "Send a message to start the conversation": "Invia un messaggio per iniziare la conversazione",
"Send Anyway": "Invia comunque",
"Send message request failed": "Richiesta di invio messaggio non riuscita",
"Send poll": "Invia sondaggio",
diff --git a/src/i18n/ja.json b/src/i18n/ja.json
index d82ab8ba75..41f4462eb9 100644
--- a/src/i18n/ja.json
+++ b/src/i18n/ja.json
@@ -244,6 +244,7 @@
"Select up to {{count}}_one": "最大{{count}}まで選択",
"Select up to {{count}}_other": "最大{{count}}まで選択",
"Send": "送信",
+ "Send a message to start the conversation": "メッセージを送って会話を始めましょう",
"Send Anyway": "とにかく送信する",
"Send message request failed": "メッセージ送信リクエストが失敗しました",
"Send poll": "アンケートを送信",
diff --git a/src/i18n/ko.json b/src/i18n/ko.json
index 03ce7876fd..66be31f380 100644
--- a/src/i18n/ko.json
+++ b/src/i18n/ko.json
@@ -244,6 +244,7 @@
"Select up to {{count}}_one": "{{count}}개까지 선택",
"Select up to {{count}}_other": "{{count}}개까지 선택",
"Send": "보내다",
+ "Send a message to start the conversation": "메시지를 보내 대화를 시작하세요",
"Send Anyway": "어쨌든 보내기",
"Send message request failed": "메시지 보내기 요청 실패",
"Send poll": "투표 보내기",
diff --git a/src/i18n/nl.json b/src/i18n/nl.json
index f87aa0d76c..17ef62e7e2 100644
--- a/src/i18n/nl.json
+++ b/src/i18n/nl.json
@@ -244,6 +244,7 @@
"Select up to {{count}}_one": "Selecteer tot {{count}}",
"Select up to {{count}}_other": "Selecteer tot {{count}}",
"Send": "Verstuur",
+ "Send a message to start the conversation": "Stuur een bericht om het gesprek te starten",
"Send Anyway": "Toch versturen",
"Send message request failed": "Verzoek om bericht te verzenden mislukt",
"Send poll": "Peiling versturen",
diff --git a/src/i18n/pt.json b/src/i18n/pt.json
index f7833f819d..94869abfb3 100644
--- a/src/i18n/pt.json
+++ b/src/i18n/pt.json
@@ -253,6 +253,7 @@
"Select up to {{count}}_many": "Selecionar até {{count}}",
"Select up to {{count}}_other": "Selecionar até {{count}}",
"Send": "Enviar",
+ "Send a message to start the conversation": "Envie uma mensagem para iniciar a conversa",
"Send Anyway": "Enviar de qualquer forma",
"Send message request failed": "O pedido de envio da mensagem falhou",
"Send poll": "Enviar enquete",
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index e3d4dd9986..b53b1818f4 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -262,6 +262,7 @@
"Select up to {{count}}_many": "Выберите до {{count}}",
"Select up to {{count}}_other": "Выберите до {{count}}",
"Send": "Отправить",
+ "Send a message to start the conversation": "Отправьте сообщение, чтобы начать разговор",
"Send Anyway": "Мне всё равно, отправить",
"Send message request failed": "Не удалось отправить запрос на отправку сообщения",
"Send poll": "Отправить опрос",
diff --git a/src/i18n/tr.json b/src/i18n/tr.json
index e2689aeabf..549504fdb9 100644
--- a/src/i18n/tr.json
+++ b/src/i18n/tr.json
@@ -244,6 +244,7 @@
"Select up to {{count}}_one": "En fazla {{count}}'yi seçin",
"Select up to {{count}}_other": "En fazla {{count}}'yi seçin",
"Send": "Gönder",
+ "Send a message to start the conversation": "Sohbete başlamak için bir mesaj gönderin",
"Send Anyway": "Yine de gönder",
"Send message request failed": "Mesaj gönderme isteği başarısız oldu",
"Send poll": "Anketi gönder",
diff --git a/src/styling/index.scss b/src/styling/index.scss
index ab73b32ae3..8905230671 100644
--- a/src/styling/index.scss
+++ b/src/styling/index.scss
@@ -29,6 +29,7 @@
@use '../components/Channel/styling' as Channel;
@use '../components/ChatView/styling' as ChatView;
@use '../components/DateSeparator/styling' as DateSeparator;
+@use '../components/EmptyStateIndicator/styling' as EmptyStateIndicator;
@use '../components/Gallery/styling' as Gallery;
@use '../components/MediaRecorder/AudioRecorder/styling' as AudioRecorder;
@use '../components/Message/styling' as Message;
From 362bc511d4b8a33c8e09d0f90c3aa61387f10cef Mon Sep 17 00:00:00 2001
From: martincupela
Date: Mon, 23 Feb 2026 10:49:49 +0100
Subject: [PATCH 11/11] feat: redesign EmptyStateIndicator for message list
---
src/components/Channel/styling/Channel.scss | 24 -----------------
.../EmptyStateIndicator.tsx | 3 ++-
.../styling/EmptyStateIndicator.scss | 27 +++++++++++--------
src/i18n/de.json | 1 +
src/i18n/en.json | 1 +
src/i18n/es.json | 1 +
src/i18n/fr.json | 1 +
src/i18n/hi.json | 1 +
src/i18n/it.json | 1 +
src/i18n/ja.json | 1 +
src/i18n/ko.json | 1 +
src/i18n/nl.json | 1 +
src/i18n/pt.json | 1 +
src/i18n/ru.json | 1 +
src/i18n/tr.json | 1 +
15 files changed, 30 insertions(+), 36 deletions(-)
diff --git a/src/components/Channel/styling/Channel.scss b/src/components/Channel/styling/Channel.scss
index 27aae65a27..23db8ca007 100644
--- a/src/components/Channel/styling/Channel.scss
+++ b/src/components/Channel/styling/Channel.scss
@@ -21,17 +21,6 @@
}
}
-.str-chat__empty-channel {
- --str-chat-icon-height: calc(var(--str-chat__spacing-px) * 136);
- @include utils.empty-layout;
- position: relative;
-
- .str-chat__empty-channel-notifications {
- position: absolute;
- inset-block-end: var(--str-chat__spacing-2);
- }
-}
-
.str-chat__loading-channel {
$text-height: calc(var(--str-chat__spacing-px) * 16);
height: 100%;
@@ -169,9 +158,6 @@
/* The icon color used when no channel is selected */
--str-chat__channel-empty-indicator-color: var(--str-chat__disabled-color);
- /* The text color used when no channel is selected */
- --str-chat__channel-empty-color: var(--str-chat__text-low-emphasis-color);
-
/* The color of the loading indicator */
--str-chat__channel-loading-state-color: var(--str-chat__disabled-color);
}
@@ -180,16 +166,6 @@
@include utils.component-layer-overrides('channel');
}
-.str-chat__empty-channel {
- --str-chat-icon-color: var(--str-chat__channel-empty-color);
- @include utils.component-layer-overrides('channel');
- @include utils.empty-theme('channel');
-
- .str-chat__empty-channel-text {
- color: var(--str-chat__channel-empty-color);
- }
-}
-
.str-chat__loading-channel {
@include utils.loading-animation;
diff --git a/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx b/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx
index 881cb1240a..95c93cd097 100644
--- a/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx
+++ b/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { useTranslationContext } from '../../context/TranslationContext';
import { ChatBubble } from './icons';
+import { IconBubble3ChatMessage } from '../Icons';
export type EmptyStateIndicatorProps = {
/** List Type: channel | message */
@@ -31,7 +32,7 @@ const UnMemoizedEmptyStateIndicator = (props: EmptyStateIndicatorProps) => {
const text = t('Send a message to start the conversation');
return (
-
+
{text}
diff --git a/src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss b/src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss
index f2b3f57573..2f18457a25 100644
--- a/src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss
+++ b/src/components/EmptyStateIndicator/styling/EmptyStateIndicator.scss
@@ -1,4 +1,10 @@
-// Figma design: empty channel only (str-chat__empty-channel). str-chat__channel-list-empty unchanged.
+@use '../../../styling/utils';
+
+.str-chat {
+ /* The text color used when no channel is selected */
+ --str-chat__channel-empty-color: var(--text-secondary);
+}
+
.str-chat__empty-channel {
--str-chat-icon-color: var(--text-tertiary);
display: flex;
@@ -6,18 +12,18 @@
align-items: center;
justify-content: center;
height: 100%;
- padding: var(--str-chat__spacing-4);
+ padding: var(--spacing-md);
position: relative;
- gap: var(--str-chat__spacing-3); // 12px – Figma spacing/sm
+ gap: var(--spacing-xs);
text-align: center;
+ @include utils.component-layer-overrides('channel');
+ color: var(--text-secondary);
+ font: var(--str-chat__caption-default-text);
svg {
- width: calc(var(--str-chat__spacing-px) * 136);
- height: calc(var(--str-chat__spacing-px) * 136);
-
- path {
- fill: var(--text-tertiary);
- }
+ width: 32px;
+ height: 32px;
+ color: var(--str-chat-icon-color);
}
.str-chat__empty-channel-notifications {
@@ -26,8 +32,7 @@
}
.str-chat__empty-channel-text {
- font: 400 14px / 20px var(--str-chat__font-family); // Figma caption/default
- color: var(--text-tertiary);
margin: 0;
+ max-width: 160px;
}
}
diff --git a/src/i18n/de.json b/src/i18n/de.json
index 2995a44db1..f77b8ef249 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -250,6 +250,7 @@
"Select up to {{count}}_one": "Bis zu {{count}} auswählen",
"Select up to {{count}}_other": "Bis zu {{count}} auswählen",
"Send": "Senden",
+ "Send a message to start the conversation": "Senden Sie eine Nachricht, um die Unterhaltung zu beginnen",
"Send Anyway": "Trotzdem senden",
"Send message request failed": "Senden der Nachrichtenanfrage fehlgeschlagen",
"Send poll": "Umfrage senden",
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 449e320a25..7202fc5b93 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -250,6 +250,7 @@
"Select up to {{count}}_one": "Select up to {{count}}",
"Select up to {{count}}_other": "Select up to {{count}}",
"Send": "Send",
+ "Send a message to start the conversation": "Send a message to start the conversation",
"Send Anyway": "Send Anyway",
"Send message request failed": "Send message request failed",
"Send poll": "Send poll",
diff --git a/src/i18n/es.json b/src/i18n/es.json
index 742831c68d..a9abb72256 100644
--- a/src/i18n/es.json
+++ b/src/i18n/es.json
@@ -259,6 +259,7 @@
"Select up to {{count}}_many": "Selecciona hasta {{count}}",
"Select up to {{count}}_other": "Selecciona hasta {{count}}",
"Send": "Enviar",
+ "Send a message to start the conversation": "Envía un mensaje para iniciar la conversación",
"Send Anyway": "Enviar de todos modos",
"Send message request failed": "Error al enviar la solicitud de mensaje",
"Send poll": "Enviar encuesta",
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index f31621c2c0..86750a11d4 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -259,6 +259,7 @@
"Select up to {{count}}_many": "Sélectionner jusqu'à {{count}}",
"Select up to {{count}}_other": "Sélectionner jusqu'à {{count}}",
"Send": "Envoyer",
+ "Send a message to start the conversation": "Envoyez un message pour commencer la conversation",
"Send Anyway": "Envoyer quand même",
"Send message request failed": "Échec de la demande d'envoi de message",
"Send poll": "Envoyer le sondage",
diff --git a/src/i18n/hi.json b/src/i18n/hi.json
index 70c85f70a3..cb06e874fd 100644
--- a/src/i18n/hi.json
+++ b/src/i18n/hi.json
@@ -251,6 +251,7 @@
"Select up to {{count}}_one": "अधिकतम {{count}} तक चुनें",
"Select up to {{count}}_other": "अधिकतम {{count}} तक चुनें",
"Send": "भेजे",
+ "Send a message to start the conversation": "बातचीत शुरू करने के लिए संदेश भेजें",
"Send Anyway": "वैसे भी भेजें",
"Send message request failed": "संदेश भेजने का अनुरोध विफल रहा",
"Send poll": "पोल भेजें",
diff --git a/src/i18n/it.json b/src/i18n/it.json
index 3d8659da0f..dcdb0a1a6a 100644
--- a/src/i18n/it.json
+++ b/src/i18n/it.json
@@ -259,6 +259,7 @@
"Select up to {{count}}_many": "Seleziona fino a {{count}}",
"Select up to {{count}}_other": "Seleziona fino a {{count}}",
"Send": "Invia",
+ "Send a message to start the conversation": "Invia un messaggio per iniziare la conversazione",
"Send Anyway": "Invia comunque",
"Send message request failed": "Richiesta di invio messaggio non riuscita",
"Send poll": "Invia sondaggio",
diff --git a/src/i18n/ja.json b/src/i18n/ja.json
index 813fa5dbd6..65c3490ef0 100644
--- a/src/i18n/ja.json
+++ b/src/i18n/ja.json
@@ -250,6 +250,7 @@
"Select up to {{count}}_one": "最大{{count}}まで選択",
"Select up to {{count}}_other": "最大{{count}}まで選択",
"Send": "送信",
+ "Send a message to start the conversation": "メッセージを送って会話を始めましょう",
"Send Anyway": "とにかく送信する",
"Send message request failed": "メッセージ送信リクエストが失敗しました",
"Send poll": "アンケートを送信",
diff --git a/src/i18n/ko.json b/src/i18n/ko.json
index 929529d107..4651a989ff 100644
--- a/src/i18n/ko.json
+++ b/src/i18n/ko.json
@@ -250,6 +250,7 @@
"Select up to {{count}}_one": "{{count}}개까지 선택",
"Select up to {{count}}_other": "{{count}}개까지 선택",
"Send": "보내다",
+ "Send a message to start the conversation": "대화를 시작하려면 메시지를 보내세요",
"Send Anyway": "어쨌든 보내기",
"Send message request failed": "메시지 보내기 요청 실패",
"Send poll": "투표 보내기",
diff --git a/src/i18n/nl.json b/src/i18n/nl.json
index 689960ea87..428d9c7096 100644
--- a/src/i18n/nl.json
+++ b/src/i18n/nl.json
@@ -250,6 +250,7 @@
"Select up to {{count}}_one": "Selecteer tot {{count}}",
"Select up to {{count}}_other": "Selecteer tot {{count}}",
"Send": "Verstuur",
+ "Send a message to start the conversation": "Stuur een bericht om het gesprek te beginnen",
"Send Anyway": "Toch versturen",
"Send message request failed": "Verzoek om bericht te verzenden mislukt",
"Send poll": "Peiling versturen",
diff --git a/src/i18n/pt.json b/src/i18n/pt.json
index 80ef00bad8..ef65559597 100644
--- a/src/i18n/pt.json
+++ b/src/i18n/pt.json
@@ -259,6 +259,7 @@
"Select up to {{count}}_many": "Selecionar até {{count}}",
"Select up to {{count}}_other": "Selecionar até {{count}}",
"Send": "Enviar",
+ "Send a message to start the conversation": "Envie uma mensagem para iniciar a conversa",
"Send Anyway": "Enviar de qualquer forma",
"Send message request failed": "O pedido de envio da mensagem falhou",
"Send poll": "Enviar enquete",
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index c276654139..e068ba2c70 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -268,6 +268,7 @@
"Select up to {{count}}_many": "Выберите до {{count}}",
"Select up to {{count}}_other": "Выберите до {{count}}",
"Send": "Отправить",
+ "Send a message to start the conversation": "Отправьте сообщение, чтобы начать разговор",
"Send Anyway": "Мне всё равно, отправить",
"Send message request failed": "Не удалось отправить запрос на отправку сообщения",
"Send poll": "Отправить опрос",
diff --git a/src/i18n/tr.json b/src/i18n/tr.json
index 63749a6793..3f8d2a9bf0 100644
--- a/src/i18n/tr.json
+++ b/src/i18n/tr.json
@@ -250,6 +250,7 @@
"Select up to {{count}}_one": "En fazla {{count}}'yi seçin",
"Select up to {{count}}_other": "En fazla {{count}}'yi seçin",
"Send": "Gönder",
+ "Send a message to start the conversation": "Sohbete başlamak için bir mesaj gönderin",
"Send Anyway": "Yine de gönder",
"Send message request failed": "Mesaj gönderme isteği başarısız oldu",
"Send poll": "Anketi gönder",