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",