From f211050b6cbf14a846b2affdfd1a33515756ce51 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Mon, 29 Mar 2021 11:24:52 +0200 Subject: [PATCH 01/28] Translations update from Weblate (#7776) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Spanish) Currently translated at 98.7% (4436 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Translated using Weblate (Spanish) Currently translated at 98.0% (4404 of 4490 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Spanish) Currently translated at 98.7% (4436 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Translated using Weblate (Spanish) Currently translated at 98.0% (4404 of 4490 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (French) Currently translated at 90.8% (4080 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/fr/ Translated using Weblate (French) Currently translated at 90.5% (4066 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/fr/ * Translated using Weblate (Dutch) Currently translated at 100.0% (4491 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4491 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Russian) Currently translated at 91.9% (4129 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ru/ Translated using Weblate (Russian) Currently translated at 90.9% (4084 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ru/ * Added translation using Weblate (English (Australia)) * Translated using Weblate (English (Australia)) Currently translated at 100.0% (4491 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/en_AU/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 97.7% (4389 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 90.5% (4066 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hant/ * Translated using Weblate (Korean) Currently translated at 79.2% (3560 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ * Deleted translation using Weblate (English (Australia)) * Translated using Weblate (French) Currently translated at 90.7% (4077 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/fr/ * Translated using Weblate (Japanese) Currently translated at 99.5% (4472 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Translated using Weblate (Swedish) Currently translated at 100.0% (4491 of 4491 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/sv/ Co-authored-by: jesus.espino Co-authored-by: Guillermo Vayá Co-authored-by: Nathanaël Co-authored-by: Tom De Moor Co-authored-by: Kaya Zeren Co-authored-by: Edward Smirnov Co-authored-by: Matthew Williams Co-authored-by: aeomin Co-authored-by: Yao Xie Co-authored-by: teamzamong Co-authored-by: Pierre JENICOT Co-authored-by: YorimiMochida Co-authored-by: MArtin Johnson --- i18n/es.json | 81 +++++++++++++++++++++++++++++++++++++------------ i18n/fr.json | 25 +++++++++++++-- i18n/ja.json | 11 +++++++ i18n/ko.json | 27 +++++++++++------ i18n/nl.json | 1 + i18n/ru.json | 58 ++++++++++++++++++++++++++++++++--- i18n/sv.json | 3 +- i18n/tr.json | 1 + i18n/zh-CN.json | 2 +- i18n/zh-TW.json | 1 + 10 files changed, 172 insertions(+), 38 deletions(-) diff --git a/i18n/es.json b/i18n/es.json index 18f93f8666f2..647938f5a6e1 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -32,6 +32,7 @@ "accessibility.sections.channelHeader": "región encabezado del canal", "accessibility.sections.lhsHeader": "región menú de equipo", "accessibility.sections.lhsList": "región barra lateral de canales", + "accessibility.sections.lhsNavigator": "región de navegación de canales", "accessibility.sections.rhs": "región complementaria {regionTitle}", "accessibility.sections.rhsContent": "región complementaria detalles del mensaje", "accessibility.sections.rhsFooter": "región entrada de respuesta", @@ -259,6 +260,7 @@ "admin.billing.payment_info_edit.serverError": "Se produjo un error al guardar la información de pago", "admin.billing.payment_info_edit.title": "Editar información de pago", "admin.billing.subscription.cancelSubscriptionSection.contactUs": "Contactar con nosotros", + "admin.billing.subscription.cancelSubscriptionSection.description": "Ahora mismo, borrar un espacio de trabajo solo se puede hacer con la ayuda de alguien del servicio de soporte para clientes.", "admin.billing.subscription.cancelSubscriptionSection.title": "Cancelar su suscripción", "admin.billing.subscription.creditCardExpired": "Tu tarjeta de crédito ha caducado. Actualiza la información de pago para evitar interrupciones.", "admin.billing.subscription.creditCardHasExpired": "Tu tarjeta de crédito ha caducado", @@ -290,6 +292,7 @@ "admin.billing.subscription.planDetails.numberOfSeats": "{numberOfSeats} asientos", "admin.billing.subscription.planDetails.numberOfSeatsRegistered": "({userCount} registrados actualmente)", "admin.billing.subscription.planDetails.perUserPerMonth": "/usuario/mes", + "admin.billing.subscription.planDetails.planDetailsName.freeForXOrMoreUsers": "/usuario/mes para {aboveUserLimit} o más usuarios.", "admin.billing.subscription.planDetails.planDetailsName.freeUpTo": "Gratis para hasta {aboveUserLimit} usuarios.", "admin.billing.subscription.planDetails.prolongedOverages": "Los excesos prolongados pueden resultar en cargos adicionales.", "admin.billing.subscription.planDetails.seatCountOverages": "Exceso en cantidad de asientos", @@ -434,7 +437,7 @@ "admin.cluster.StreamingPort": "Puerto de Transmisión:", "admin.cluster.StreamingPortDesc": "El puerto utilizado para transmitir data entre servidores.", "admin.cluster.StreamingPortEx": "Ej.: \"8075\"", - "admin.cluster.UseExperimentalGossip": "Utilizar Murmuración Experimental:", + "admin.cluster.UseExperimentalGossip": "Utilizar Murmuración:", "admin.cluster.UseExperimentalGossipDesc": "Cuando es verdadero, el servidor intentará comunicarse a través del protocolo de murmuración utilizando el puerto de murmuración. Cuando es falso el servidor intentará comunicarse a través del puerto de transmisión. Cuando es falso el puerto y protocolo de murmuración se siguen utilizando para determinar la salud del cluster.", "admin.cluster.UseIpAddress": "Usar Dirección IP:", "admin.cluster.UseIpAddressDesc": "Cuando es verdadero, el cluster intentará comunicarse a través de direcciones IP en vez de utilizar nombres de host.", @@ -552,6 +555,9 @@ "admin.customization.gfycatApiSecretDescription": "La llave secreta del API generado por Gfycat para tu identificador del API. Si permanece en blank, se utilizará la llave secreta predeterminada suministrada por Gfycat.", "admin.customization.iosAppDownloadLinkDesc": "Agrega un enlace para descargar la aplicación para iOS. Los usuarios que tienen acceso al sitio en un navegador de web móvil serán presentados con una página que les da la opción de descargar la aplicación. Deja este campo en blanco para evitar que la página aparezca.", "admin.customization.iosAppDownloadLinkTitle": "Enlace de Descarga de la Aplicación para iOS:", + "admin.customization.restrictLinkPreviewsDesc": "La previsualización de enlaces e imágenes no se mostrará para la lista de dominios separados por comas de aquí arriba.", + "admin.customization.restrictLinkPreviewsExample": "Ej.: \"internal.mycompany.com, images.example.com\"", + "admin.customization.restrictLinkPreviewsTitle": "Deshabilitar la previsualización de enlaces para estos dominios:", "admin.data_grid.empty": "No se encontraron elementos", "admin.data_grid.loading": "Cargando", "admin.data_grid.paginatorCount": "{startCount, number} - {endCount, number} de {total, number}", @@ -565,6 +571,9 @@ "admin.data_retention.confirmChangesModal.title": "Confirmar la política de retención de datos", "admin.data_retention.createJob.help": "Inicia un trabajo de eliminación de Retención de Datos inmediatamente.", "admin.data_retention.createJob.title": "Ejecutar el Trabajo de Eliminación Ahora", + "admin.data_retention.customPolicies.addPolicy": "Añadir política", + "admin.data_retention.customPolicies.subTitle": "Personalizar durante cuanto guardaran los mensajes equipos y canales específicos.", + "admin.data_retention.customPolicies.title": "Políticas de retención personalizadas", "admin.data_retention.deletionJobStartTime.description": "Establece la hora de inicio diaria para la ejecución del trabajo de retención de datos. Elige un momento en el que menos personas estén usando el sistema. Debe ser una hora en horario de 24 horas en la forma HH:MM.", "admin.data_retention.deletionJobStartTime.example": "Ej.: \"20:00\"", "admin.data_retention.deletionJobStartTime.title": "Hora de Eliminación de Datos:", @@ -708,7 +717,7 @@ "admin.experimental.clientSideCertCheck.title": "Método de Inicio de Sesión con un Certificado del lado del Cliente:", "admin.experimental.clientSideCertEnable.desc": "Habilita el inicio de sesión con un certificado del lado del cliente a tu servidor Mattermost. Lee la [documentación](!https://docs.mattermost.com/deployment/certificate-based-authentication.html) para conocer más.", "admin.experimental.clientSideCertEnable.title": "Habilitar inicio de sesión con un certificado del lado del Cliente:", - "admin.experimental.closeUnusedDirectMessages.desc": "Cuando es verdadero, las conversaciones en mensajes directos que no tienen actividad en un período de 7 días serán escondidas de la barra lateral. Cuando es falso, las conversaciones permanecerán en la barra lateral hasta que se cierren de forma manual.", + "admin.experimental.closeUnusedDirectMessages.desc": "Cuando es verdadero, las conversaciones en mensajes directos que no tienen actividad en un período de 7 días serán escondidas de la barra lateral. Cuando es falso, las conversaciones permanecerán en la barra lateral hasta que se cierren de forma manual. Esta configuración solo esta disponible si **Habilitar Barra Lateral Antigua** esta **Habilitada**.", "admin.experimental.closeUnusedDirectMessages.title": "Cerrar mensajes directos en la barra lateral de forma automática:", "admin.experimental.defaultTheme.desc": "Establece un tema predeterminado que será aplicado a todos los usuarios nuevos del sistema.", "admin.experimental.defaultTheme.title": "Tema Predeterminado:", @@ -726,6 +735,8 @@ "admin.experimental.emailSettingsLoginButtonTextColor.title": "Color del texto del botón de inicio de sesión por correo electrónico:", "admin.experimental.enableChannelViewedMessages.desc": "Este ajuste determina si los mensajes `channel_viewed` del WebSocket, esto sincroniza las notificaciones sin leer a través de los clientes y dispositivos. La desactivación de la configuración en las implementaciones de mayor tamaño puede mejorar el rendimiento del servidor.", "admin.experimental.enableChannelViewedMessages.title": "Habilitar Mensajes del WebSocket para Canal Visto:", + "admin.experimental.enableLegacySidebar.desc": "Cuando está activo, los usuarios no puede acceder a las nuevas funcionalidades de la barra lateral, incluyendo categorías personalizadas y colapsables y filtrado de canales no leídos. Recomendamos solo habilitar la barra lateral antigua si los usuarios esta experimentando fallos.", + "admin.experimental.enableLegacySidebar.title": "Habilitar Barra Lateral Antigua", "admin.experimental.enablePreviewFeatures.desc": "Cuando es verdadero, las características preliminares pueden ser habilitadas en **Configuración de la cuenta > Avanzada > Previsualizar características de pre-lanzamiento**. Cuando es falso, estas características son inhabilitadas y se esconde la opción en **Configuración de la cuenta > Avanzada > Previsualizar características de pre-lanzamiento**.", "admin.experimental.enablePreviewFeatures.title": "Habilitar Características Preliminares:", "admin.experimental.enableThemeSelection.desc": "Habilita la pestaña **Visualización > Tema** en Configuración de la Cuenta para que los usuarios puedan escoger su tema.", @@ -736,9 +747,9 @@ "admin.experimental.enableUserDeactivation.title": "Habilitar Desactivación de Cuenta:", "admin.experimental.enableUserTypingMessages.desc": "Este ajuste determina si los mensajes de \"el usuario está escribiendo...\" se muestran debajo del cuadro de mensaje. La desactivación de la configuración en las implementaciones de mayor tamaño puede mejorar el rendimiento del servidor.", "admin.experimental.enableUserTypingMessages.title": "Habilitar Usuario está escribiendo Mensajes:", - "admin.experimental.enableXToLeaveChannelsFromLHS.desc": "Cuando es verdadero, los usuarios pueden abandonar canales Públicos y Privados clickando la \"x\" al lado del nombre del canal. Cuando es falso, los usuarios deben utilizar la opción de **Abandonar Canal** del menú para poder abandonar los canales.", + "admin.experimental.enableXToLeaveChannelsFromLHS.desc": "Cuando es verdadero, los usuarios pueden abandonar canales Públicos y Privados clickando la \"x\" al lado del nombre del canal. Cuando es falso, los usuarios deben utilizar la opción de **Abandonar Canal** del menú para poder abandonar los canales. Esta configuración solo esta disponible si **Habilitar Barra Lateral Antigua** esta **Habilitada**.", "admin.experimental.enableXToLeaveChannelsFromLHS.title": "Habilitar X para abandonar Canales desde el Panel Lateral Izquierdo:", - "admin.experimental.experimentalChannelOrganization.desc": "Habilita las opciones de organización de canales en la barra lateral en **Configuración de la Cuenta > Barra lateral > Agrupación y ordenamiento de Canales** que incluye opciones para agrupar por canales no leídos, ordenar por los más recientes y combinar todos los tipos de canales en una sola lista. Estos ajustes no están disponibles si **Configuración de la Cuenta > Barra lateral > Características Experimentales** está habilitado.", + "admin.experimental.experimentalChannelOrganization.desc": "Habilita las opciones de organización de canales en la barra lateral en **Configuración de la Cuenta > Barra lateral > Agrupación y ordenamiento de Canales** que incluye opciones para agrupar por canales no leídos, ordenar por los más recientes y combinar todos los tipos de canales en una sola lista. Esta configuración solo esta disponible si **Habilitar Barra Lateral Antigua** esta **Habilitada**.", "admin.experimental.experimentalChannelOrganization.title": "Agrupación y ordenamiento de Canales", "admin.experimental.experimentalEnableAuthenticationTransfer.desc": "Cuando es verdadero, los usuarios pueden cambiar el método de inicio de sesión a cualquier método habilitado en el servidor, sea a través de Configuración de la cuenta o a través de las APIs. Cuando es falso, los Usuarios no podrán cambiar el método de inicio de sesión sin importar que opciones de autenticación estén habilitadas.", "admin.experimental.experimentalEnableAuthenticationTransfer.title": "Habilitar Transferencia de Autenticación:", @@ -749,7 +760,7 @@ "admin.experimental.experimentalEnableHardenedMode.desc": "Habilitar el modo de endurecimiento para Mattermost que hace que la experiencia de usuario sea compensada por el interés en seguridad. Ver [documentation](!https://docs.mattermost.com/administration/config-settings.html#enable-hardened-mode-experimental) para aprender más.", "admin.experimental.experimentalEnableHardenedMode.title": "Habilitar Modo de Endurecimiento:", "admin.experimental.experimentalFeatures": "Características Experimentales", - "admin.experimental.experimentalHideTownSquareinLHS.desc": "Cuando es verdadero, oculta Town Square en la barra lateral izquierda si no hay mensajes no leídos en el canal. Cuando es falso, Town Square sera siempre visible en la barra lateral incluso si todos los mensajes han sido leídos.", + "admin.experimental.experimentalHideTownSquareinLHS.desc": "Cuando es verdadero, oculta Town Square en la barra lateral izquierda si no hay mensajes no leídos en el canal. Cuando es falso, Town Square sera siempre visible en la barra lateral incluso si todos los mensajes han sido leídos. Esta configuración solo esta disponible si **Habilitar Barra Lateral Antigua** esta **Habilitada**.", "admin.experimental.experimentalHideTownSquareinLHS.title": "Town Square oculto en la barra lateral izquierda:", "admin.experimental.experimentalPrimaryTeam.desc": "El equipo principal del cual los usuarios del servidor son miembros. Cuando se establece un equipo principal, las opciones de unirse a otros equipos o de abandonar el equipo principal están deshabilitadas.", "admin.experimental.experimentalPrimaryTeam.example": "Ej.: \"nombredeequipo\"", @@ -852,7 +863,7 @@ "admin.gitlab.siteUrlExample": "Ej.: https://", "admin.gitlab.tokenTitle": "Url para obteción de Token:", "admin.gitlab.userTitle": "URL para obtener datos de usuario:", - "admin.google.EnableMarkdownDesc": "1. [Inicia sesión](!https://accounts.google.com/login) con tu cuenta de Google.\n2. Dirígete a [https://console.developers.google.com](!https://console.developers.google.com), haz clic en **Credenciales** en el panel lateral izquierdo e ingresa \"Mattermost - el-nombre-de-tu-empresa\" como **Nombre del Proyecto** y luego haz clic en **Crear**\n3. Haz clic en el encabezado **Pantalla de consentimiento de OAuth** e ingresa \"Mattermost\" como el **Nombre del producto a ser mostrado a los usuarios** y luego haz clic en **Guardar**.\n4. En el encabezado **Credenciales**, haz clic en **Crear credenciales**, escoge **ID de Cliente OAuth** y selecciona **Aplicación Web**.\n5. En **Restricciones** y **URI Autorizados de Redirección** ingresa **tu-url-de-mattermost/signup/google/complete** (ejemplo: http://localhost:8065/signup/google/complete). Haz clic en **Crear**.\n6. Pega el **ID de Cliente** y **Clave de Cliente** en los campos que se encuentran abajo y luego haz clic en **Guardar**.\n7. Dirígete a [Google People API](!https://console.developers.google.com/apis/library/people.googleapis.com) y haz clic en *Habilitar*.", + "admin.google.EnableMarkdownDesc": "1. [Inicia sesión](!https://accounts.google.com/login) con tu cuenta de Google.\n2. Dirígete a [https://console.developers.google.com](!https://console.developers.google.com), haz clic en **Credenciales** en el panel lateral izquierdo.\n3. En el encabezado **Credenciales**, haz clic en **Crear credenciales**, escoge **ID de Cliente OAuth** y selecciona **Aplicación Web**.\n4. Introduzca en \"Mattermost - su-nombre-de-empresa\" como el **Nombre**.\n5.En **URI Autorizados de Redirección** ingresa **tu-url-de-mattermost/signup/google/complete** (ejemplo: http://localhost:8065/signup/google/complete). Haz clic en **Crear**.\n6. Pega el **ID de Cliente** y **Clave de Cliente** en los campos que se encuentran abajo y luego haz clic en **Guardar**.\n7. Dirígete a [Google People API](!https://console.developers.google.com/apis/library/people.googleapis.com) y haz clic en *Habilitar*.", "admin.google.authTitle": "URL para autentificación:", "admin.google.clientIdDescription": "El ID de Cliente que recibiste al registrar la aplicación con Google.", "admin.google.clientIdExample": "Ej.: \"7602141235235-url0fhs1mayfasbmop5qlfns8dh4.apps.googleusercontent.com\"", @@ -1119,6 +1130,12 @@ "admin.license.keyRemove": "Quitar la Licencia Empresarial y Degradar el Servidor", "admin.license.noFile": "No se cargó ningún archivo", "admin.license.removing": "Quitando Licencia...", + "admin.license.renewalCard.description": "Renueva tu licencia Enterprise a través del Portal de Clientes para evitar cualquier corte en el servicio.", + "admin.license.renewalCard.licenseExpired": "Fecha de expiración de la licencia {date, date, long}.", + "admin.license.renewalCard.licenseExpiring": "La Licencia expira en {days} días, el {date, date, long}.", + "admin.license.renewalCard.licensedUsersNum": "**Usuario con Licencia:** {licensedUsersNum}", + "admin.license.renewalCard.reviewNumbers": "Revisa los numeros de abajo para asegurar que la renovación es por el número de usuarios correcto.", + "admin.license.renewalCard.usersNumbers": "**Usuarios Activos:** {activeUsersNum}", "admin.license.title": "Edición y Licencia", "admin.license.trial-request.accept-terms": "Al hacer clic en **Iniciar prueba**, aceptas el [Acuerdo de Evaluación del Software Mattermost] (!https://mattermost.com/software-evaluation-agreement/), [Política de privacidad] (!https://mattermost.com/privacy-policy/) y recibir correos electrónicos del producto.", "admin.license.trial-request.error": "La licencia de prueba no pudo ser obtenida. Visita [https://mattermost.com/trial/](https://mattermost.com/trial/) para solicitar una licencia.", @@ -1150,7 +1167,7 @@ "admin.log.locationPlaceholder": "Ingresar locación de archivo", "admin.log.locationTitle": "Directorio del Archivo de Registro:", "admin.log.logLevel": "Nivel de registros", - "admin.logs.bannerDesc": "Para buscar usuarios por ID del usuario o ID del Token, dirigete a Reportes > Usuarios y copia el ID en el filtro de búsqueda.", + "admin.logs.bannerDesc": "Para buscar usuarios por ID del usuario o ID del Token, dirigete a Gestión de usuarios > Usuarios y copia el ID en el filtro de búsqueda.", "admin.logs.next": "Siguiente", "admin.logs.prev": "Anterior", "admin.logs.reload": "Recargar", @@ -1894,6 +1911,9 @@ "admin.site.posts": "Mensajes", "admin.site.public_links": "Enlaces Públicos", "admin.site.usersAndTeams": "Usuarios y Equipos", + "admin.sql.connMaxIdleTimeDescription": "Tiempo máximo de inactividad (en milisegundos) para conectarse a la base de datos.", + "admin.sql.connMaxIdleTimeExample": "P.Ej.: \"300000\"", + "admin.sql.connMaxIdleTimeTitle": "Tiempo máximo de conexión inactiva:", "admin.sql.connMaxLifetimeDescription": "Tiempo máximo de duración (en milisegundos) para las conexiones a base de datos.", "admin.sql.connMaxLifetimeExample": "Ej.: \"3600000\"", "admin.sql.connMaxLifetimeTitle": "Tiempo máximo de duración de la conexión:", @@ -1964,7 +1984,9 @@ "admin.team.brandTextTitle": "Texto de la marca personalizada:", "admin.team.brandTitle": "Habilitar marca personalizada: ", "admin.team.chooseImage": "Seleccionar Imagen", - "admin.team.editOthersPostsDesc": "Cuando es verdadero, Los Administradores de Equipo y Administradores del Sistema pueden editar mensajes de otros usuarios. Si es falso, sólo los Administradores del Sistema pueden editar los mensajes de otros usuarios.", + "admin.team.customUserStatusesDescription": "Cuando es verdadero, los usuarios pueden establecer un mensaje de estado descriptivo y un emoticono visible para todos los usuarios.", + "admin.team.customUserStatusesTitle": "Habilitar Estados Personalizados: ", + "admin.team.editOthersPostsDesc": "Cuando es **verdadero**, Los Administradores de Equipo y Administradores del Sistema pueden editar mensajes de otros usuarios. Si es **falso**, sólo los Administradores del Sistema pueden editar los mensajes de otros usuarios. En cualquier caso, los Administradores de Equipo y Administradores del Sistema siempre pueden borrar los mensajes de otros usuarios.", "admin.team.editOthersPostsTitle": "Permitir que Administradores de Equipo puedan editar los mensajes de otros:", "admin.team.emailInvitationsDescription": "Cuando es verdadero un usuario puede invitar a otros utilizando el correo electrónico del sistema.", "admin.team.emailInvitationsTitle": "Habilitar Invitaciones por Correo Electrónico: ", @@ -2124,6 +2146,7 @@ "admin.user_item.menuAriaLabel": "Menú de acciones de usuario", "admin.user_item.mfaNo": "**Autenticación de Múltiples factores**: No", "admin.user_item.mfaYes": "**Autenticación de Múltiples factores**: Sí", + "admin.user_item.promoteToMember": "Promover a Miembro", "admin.user_item.resetEmail": "Actualizar Correo Electrónico", "admin.user_item.resetMfa": "Quitar MFA", "admin.user_item.resetPwd": "Reiniciar Contraseña", @@ -2185,8 +2208,8 @@ "analytics.team.totalPosts": "Total de Mensajes", "analytics.team.totalUsers": "Total de Usuarios Activos", "announcement_bar.error.email_verification_required": "Revisa tu correo electrónico para verificar la dirección.", - "announcement_bar.error.license_expired": "La licencia de Empresa expiró y algunas de las características pueden que estén desactivadas. [Por favor renovar](!{link}).", - "announcement_bar.error.license_expiring": "La licencia de Empresa expira el {date, date, long}. [Por favor renovar](!{link}).", + "announcement_bar.error.license_expired": "La licencia de Empresa expiró y algunas de las características pueden que estén desactivadas.", + "announcement_bar.error.license_expiring": "La licencia de Empresa expira el {date, date, long}.", "announcement_bar.error.past_grace": "La licencia para Empresas está vencida y algunas características pueden ser inhabilitadas. Por favor contacta a tu Administrador de Sistema para más detalles.", "announcement_bar.error.preview_mode": "Modo de prueba: Las notificaciones por correo electrónico no han sido configuradas.", "announcement_bar.error.site_url.full": "Por favor configura la [URL del Sitio](https://docs.mattermost.com/administration/config-settings.html#site-url) en la [Console de Sistema](/admin_console/environment/web_server).", @@ -2195,6 +2218,10 @@ "announcement_bar.notification.email_verified": "Correo electrónico Verificado", "announcement_bar.number_active_users_warn_metric_status.text": "Ahora tiene más de {limit} usuarios. Recomendamos encarecidamente el uso de funciones avanzadas para servidores a gran escala.", "announcement_bar.number_of_posts_warn_metric_status.text": "Ahora tienes más de {límite} mensajes. Recomendamos encarecidamente el uso de funciones avanzadas para evitar la degradación en el rendimiento.", + "announcement_bar.warn.contact_support_text": "Para renovar tu licencia, contacte con soporte en support@mattermost.com.", + "announcement_bar.warn.email_support": "[Contactar con soporte](!{email}).", + "announcement_bar.warn.no_internet_connection": "Parece que no tiene acceso a internet.", + "announcement_bar.warn.renew_license_now": "Renovar licencia ahora", "announcement_bar.warn_metric_status.number_of_posts.text": "Ahora tiene más de 2.000.000 de mensajes. Recomendamos encarecidamente el uso de funciones avanzadas para evitar la degradación en el rendimiento.", "announcement_bar.warn_metric_status.number_of_posts_ack.text": "Gracias por contactar con Mattermost. Pronto nos pondremos en contacto contigo.", "announcement_bar.warn_metric_status.number_of_users.text": "Ahora tiene más de 500 usuarios. Recomendamos encarecidamente el uso de funciones avanzadas para servidores a gran escala.", @@ -2379,7 +2406,7 @@ "channel_header.convert": "Convertir a Canal Privado", "channel_header.delete": "Archivar Canal", "channel_header.directchannel.you": "{displayname} (tu) ", - "channel_header.editLink": "(Editar)", + "channel_header.editLink": "Editar", "channel_header.flagged": "Mensajes Guardados", "channel_header.groupConstrained": "Miembros gestionados por grupos enlazados.", "channel_header.groupMessageHasGuests": "Este grupo tiene huéspedes", @@ -2543,6 +2570,11 @@ "combined_system_message.removed_from_team.one_you": "Fuiste **eliminado del equipo**.", "combined_system_message.removed_from_team.two": "{firstUser} y {secondUser} fueron **eliminados del equipo**.", "combined_system_message.you": "Tu", + "commercial_support.download_support_packet": "Descargar Paquete de Soporte", + "commercial_support.title": "Soporte Comercial", + "confirm.notification_sent_to_admin.modal_body": "Una notificación ha sido enviada a su administrador.", + "confirm.notification_sent_to_admin.modal_done": "Hecho", + "confirm.notification_sent_to_admin.modal_title": "¡Gracias!", "confirm_modal.cancel": "Cancelar", "convert_channel.cancel": "No, cancelar", "convert_channel.confirm": "Sí, convertir a canal privado", @@ -2591,6 +2623,16 @@ "create_team.team_url.unavailable": "Esta dirección URL ya está en uso ó no está disponible. Por favor, pruebe con otra.", "create_team.team_url.webAddress": "Escoge la dirección web para tu nuevo equipo:", "custom_emoji.header": "Emoticonos Personalizados", + "custom_status.modal_cancel": "Eliminar Estado", + "custom_status.modal_confirm": "Establecer Estado", + "custom_status.set_status": "Establecer un Estado", + "custom_status.suggestions.in_a_meeting": "En una reunión", + "custom_status.suggestions.on_a_vacation": "De vacaciones", + "custom_status.suggestions.out_for_lunch": "Comiendo", + "custom_status.suggestions.out_sick": "Enfermo", + "custom_status.suggestions.recent_title": "RECIENTE", + "custom_status.suggestions.title": "SUGERENCIAS", + "custom_status.suggestions.working_from_home": "Trabajando desde casa", "date_separator.today": "Hoy", "date_separator.yesterday": "Ayer", "deactivate_member_modal.deactivate": "Desactivar", @@ -2708,6 +2750,7 @@ "error.local_storage.help3": "Utiliza un navegador compatible (IE 11, Chrome 61+, Firefox 60+, Safari 12+, Edge 42+)", "error.local_storage.message": "Mattermost fue incapaz de cargar debido a una configuración en el navegador impide el uso de la característica de almacenamiento local. Para permitir que Mattermost cargue, intenta las acciones siguientes:", "error.local_storage.title": "No se puede cargar Mattermost", + "error.maxFreeUsersReached.title": "Este espacio de trabajo a alcanzado el límite de usuarios.", "error.not_found.message": "La página que está intentando acceder no existe", "error.not_found.title": "Página no encontrada", "error.oauth_access_denied": "Debes autorizar a Mattermost para iniciar sesión con {service}.", @@ -3146,7 +3189,7 @@ "invitation_modal.invite_members.description": "Invitar un nuevo miembro del equipo con un enlace o por correo electrónico. Los miembros de equipo tienen acceso a mensajes y archivos en equipos abiertos y canales públicos.", "invitation_modal.invite_members.description-email-disabled": "Invitar un nuevo miembro del equipo con un enlace. Los miembros de equipo tienen acceso a mensajes y archivos en equipos abiertos y canales públicos.", "invitation_modal.invite_members.exceeded_max_add_members_batch": "No se puede invitar a más de **{text}** personas a la vez", - "invitation_modal.invite_members.hit_cloud_user_limit": "Solo puedes invitar a **{text}** más {text, plural, one {miembro} other {miembros}} en el nivel gratuito", + "invitation_modal.invite_members.hit_cloud_user_limit": "Solo puedes invitar a **{num} más {num, plural, one {miembro} other {miembros}}** al equipo en el nivel gratuito.", "invitation_modal.invite_members.title": "Invitar **Miembros**", "invitation_modal.members.invite_button": "Invitar Miembros", "invitation_modal.members.or": "Ó", @@ -3344,13 +3387,13 @@ "multiselect.addTeamsPlaceholder": "Buscar y agregar equipos", "multiselect.adding": "Agregando...", "multiselect.go": "Ir", - "multiselect.list.notFound": "No se encontraron elementos", + "multiselect.list.notFound": "No se encontraron resultados que encajen con **{searchQuery}**", "multiselect.loading": "Cargando...", "multiselect.numGroupsRemaining": "Utiliza ↑↓ para navegar, ↵ para seleccionar. Puedes agregar {num, number} {num, plural, one {grupo} other {grupos}} más. ", "multiselect.numMembers": "{memberOptions, number} de {totalCount, number} miembros", "multiselect.numPeopleRemaining": "Utiliza ↑↓ para navegar, ↵ para seleccionar. Puedes agregar {num, number} {num, plural, one {persona} other {personas}} más. ", "multiselect.numRemaining": "Puedes agregar {num, number} más", - "multiselect.placeholder": "Buscar y agregar miembros", + "multiselect.placeholder": "Buscar personas", "multiselect.selectChannels": "Utiliza ↑↓ para navegar, ↵ para seleccionar.", "multiselect.selectTeams": "Utiliza ↑↓ para navegar, ↵ para seleccionar.", "navbar.addGroups": "Agregar Grupos", @@ -3551,9 +3594,9 @@ "postlist.toast.scrollToLatest": "Saltar a los mensajes nuevos", "posts_view.loadMore": "Cargar más mensajes", "posts_view.newMsg": "Nuevos Mensajes", - "promote_to_user_modal.desc": "Esta acción promueve al huésped {username} a miembro. Esto permitirá que el usuario se pueda unir a canales públicos y pueda interactuar con usuarios fuera de los canales de los cuales es miembro actualmente. ¿Está seguro que desea promover al huésped {username} a usuario?", + "promote_to_user_modal.desc": "Esta acción promueve al huésped {username} a miembro. Esto permitirá que el usuario se pueda unir a canales públicos y pueda interactuar con usuarios fuera de los canales de los cuales es miembro actualmente. ¿Está seguro que desea promover al huésped {username} a miembro?", "promote_to_user_modal.promote": "Promover", - "promote_to_user_modal.title": "Promover huésped {username} a usuario", + "promote_to_user_modal.title": "Promover huésped {username} a miembro", "quick_switch_modal.channels": "Canales", "quick_switch_modal.channelsShortcut.mac": "- ⌘K", "quick_switch_modal.channelsShortcut.windows": "- CTRL+K", @@ -3985,7 +4028,7 @@ "upgrade.cloud": "Actualizar Mattermost Cloud", "upgrade.cloud_banner_over": "Actualmente estás por encima del límite de usuarios del nivel gratuito", "upgrade.cloud_banner_reached": "Has alcanzado el límite de usuarios del nivel gratuito", - "upgrade.cloud_modal_body": "El nivel gratuito está limitado a 10 usuarios. Actualice Mattermost Cloud para más usuarios.", + "upgrade.cloud_modal_body": "El nivel gratuito está limitado a {num} usuarios. Actualice Mattermost Cloud para más usuarios.", "upgrade.cloud_modal_title": "Has alcanzado el límite de usuarios", "upload_overlay.info": "Arrastra un archivo para cargarlo.", "user.settings.advance.confirmDeactivateAccountTitle": "Confirmar Desactivación", @@ -4317,7 +4360,7 @@ "user.settings.sidebar.unreadsDesc": "Agrupar canales no leídos por separado hasta que sean leídos.", "user.settings.sidebar.unreadsFavoritesShort": "Canales sin leer y favoritos agrupados por separado", "user.settings.sidebar.unreadsShort": "Canales sin leer agrupados por separado", - "user.settings.timezones.automatic": "Asignar automáticamente", + "user.settings.timezones.automatic": "Automático", "user.settings.timezones.promote": "Selecciona la zona horaria para ser utilizada en la interfaz de usuario y notificaciones de correo electrónico.", "user.settings.tokens.activate": "Activado", "user.settings.tokens.cancel": "Cancelar", @@ -4355,7 +4398,7 @@ "userGuideHelp.reportAProblem": "Reportar un problema", "user_list.notFound": "No se encontraron usuarios", "user_profile.account.editSettings": "Editar Configuración de la Cuenta", - "user_profile.account.localTime": "Hora Local: ", + "user_profile.account.localTime": "Hora Local", "user_profile.account.post_was_created": "Este mensaje fue creado por una integración de", "user_profile.add_user_to_channel": "Agregar al Canal", "user_profile.add_user_to_channel.icon": "Icono de Agregar Usuario al Canal", diff --git a/i18n/fr.json b/i18n/fr.json index a339b3e85b6e..b3454085e680 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -260,6 +260,7 @@ "admin.billing.payment_info_edit.title": "Modifier les moyens de paiement", "admin.billing.subscription.cancelSubscriptionSection.contactUs": "Nous contacter", "admin.billing.subscription.cancelSubscriptionSection.description": "Pour le moment, la suppression d'un espace de travail ne peut être effectuée que par un conseiller du support client.", + "admin.billing.subscription.cancelSubscriptionSection.title": "Annuler votre abonnement", "admin.billing.subscription.creditCardExpired": "Votre carte de crédit a expiré. Mettez à jour vos moyens de paiement pour éviter toute interruption de service.", "admin.billing.subscription.creditCardHasExpired": "Votre carte de crédit a expiré", "admin.billing.subscription.creditCardHasExpired.description.avoidAnyDisruption": " pour éviter toute interruption de service.", @@ -273,6 +274,7 @@ "admin.billing.subscription.mostRecentPaymentFailed": "Votre dernier paiement a échoué", "admin.billing.subscription.nextBillingDate": "À partir de {date}, vous serez facturé en fonction du nombre d'utilisateurs activés", "admin.billing.subscription.otherBillingOption": "Besoin d'autres options de facturation ?", + "admin.billing.subscription.payamentBegins": "Le paiement débute : {beginDate}", "admin.billing.subscription.paymentFailed": "Le paiement a échoué. Veuillez réessayer ou contacter le service d'aide.", "admin.billing.subscription.paymentVerificationFailed": "Désolé, la vérification du paiement a échoué", "admin.billing.subscription.perUserPerMonth": " /utilisateur/mois", @@ -289,6 +291,7 @@ "admin.billing.subscription.planDetails.numberOfSeats": "{numberOfSeats} d'utilisateurs", "admin.billing.subscription.planDetails.numberOfSeatsRegistered": "({userCount} actuellement enregistrés)", "admin.billing.subscription.planDetails.perUserPerMonth": "/utilisateur/mois", + "admin.billing.subscription.planDetails.planDetailsName.freeUpTo": "Gratuit jusqu'à {aboveUserLimit} utilisateurs.", "admin.billing.subscription.planDetails.prolongedOverages": "Les dépassements prolongés peuvent entraîner des frais supplémentaires.", "admin.billing.subscription.planDetails.seatCountOverages": "Dépassement du nombre d'utilisateurs", "admin.billing.subscription.planDetails.startDate": "Date de début : ", @@ -303,6 +306,7 @@ "admin.billing.subscription.questions": "Des questions ?", "admin.billing.subscription.stateprovince": "État/Province", "admin.billing.subscription.title": "Abonnements", + "admin.billing.subscription.updatePaymentInfo": "Mettre à jour les informations de paiement", "admin.billing.subscription.upgrade": "Mettre à jour", "admin.billing.subscription.upgradeCloudSubscription": "Mettez à jour votre abonnement Mattermost Cloud", "admin.billing.subscription.upgradeMattermostCloud.description": "La version gratuite est **limitée à 10 utilisateurs**. Pour pouvoir créer plus d'utilisateurs, d'équipes et avoir accès à d'autres fonctions intéressantes", @@ -420,6 +424,7 @@ "admin.cluster.ClusterNameEx": "Ex. : « Production » ou « Staging »", "admin.cluster.EnableExperimentalGossipEncryption": "Activer le chiffrement expérimental du protocole de bavardage (gossip protocol) :", "admin.cluster.EnableExperimentalGossipEncryptionDesc": "Si activé, toutes les communications passant par le protocole de bavardage seront chiffrées.", + "admin.cluster.EnableGossipCompression": "Activer la compression avec le protocole Gossip :", "admin.cluster.GossipPort": "Port de bavardage :", "admin.cluster.GossipPortDesc": "Le port utilisé pour le protocole de bavardage. Seuls UDP et TCP devraient être autorisés sur ce port.", "admin.cluster.GossipPortEx": "Ex. : « 8074 »", @@ -429,7 +434,7 @@ "admin.cluster.StreamingPort": "Port de streaming :", "admin.cluster.StreamingPortDesc": "Le port utilisé pour streamer des données entre les serveurs.", "admin.cluster.StreamingPortEx": "Ex. : « 8075 »", - "admin.cluster.UseExperimentalGossip": "Utiliser le protocole de bavardage expérimental :", + "admin.cluster.UseExperimentalGossip": "Utiliser le protocole de bavardage :", "admin.cluster.UseExperimentalGossipDesc": "Lorsqu'activé, le serveur va essayer de communiquer en utilisant le protocole et le port de bavardage. Lorsque désactivé, le serveur va essayer de communiquer à l'aide du port de streaming. Lorsque désactivés, les protocole et port de bavardage sont toujours utilisés pour déterminer la santé du cluster.", "admin.cluster.UseIpAddress": "Utiliser l'adresse IP :", "admin.cluster.UseIpAddressDesc": "Lorsqu'activé, le cluster va essayer de communiquer à l'aide de l'adresse IP au lieu d'utiliser le nom d'hôte.", @@ -3491,6 +3496,7 @@ "shortcuts.nav.unread_prev": "Canal non lu précédent :\tAlt|Maj|Haut", "shortcuts.nav.unread_prev.mac": "Canal non lu précédent :\t⌥|Maj|Haut", "shortcuts.team_nav.next.mac": "Next team:\t⌘|⌥|Down", + "sidebar.allDirectMessages": "Tous les messages personnels", "sidebar.browseChannelDirectChannel": "Parcourir les canaux ou messages personnels", "sidebar.createChannel": "Créer un canal public", "sidebar.createDirectMessage": "Créer un nouveau message personnel", @@ -3502,7 +3508,12 @@ "sidebar.moreElips": "Plus...", "sidebar.morePublicAria": "plus de canaux publics", "sidebar.morePublicDmAria": "plus de canaux publics et de messages personnels", + "sidebar.openDirectMessage": "Ouvrir un message personnel", "sidebar.removeList": "Retirer de la liste", + "sidebar.show": "Afficher", + "sidebar.sort": "Trier", + "sidebar.sortedByRecencyLabel": "Activité récente", + "sidebar.sortedManually": "Manuellement", "sidebar.team_select": "{siteName} - Rejoindre une équipe", "sidebar.tutorialScreen1.body": "Les **canaux** organisent les conversations en différents sujets. Ils sont ouverts à tous les membres de votre équipe. Pour envoyer des communications privées, utilisez les **messages personnels** pour une personne seule ou les **canaux privés** pour plusieurs personnes.", "sidebar.tutorialScreen1.title": "Canaux", @@ -3528,14 +3539,17 @@ "sidebar_header.tutorial.body3": "Les administrateurs système trouveront une option **Console système** pour gérer l'ensemble du système.", "sidebar_header.tutorial.title": "Menu principal", "sidebar_left.add_channel_dropdown.createNewChannel": "Créer un nouveau canal", + "sidebar_left.channel_filter.showAllChannels": "Afficher tous les canaux", "sidebar_left.channel_navigator.channelSwitcherLabel": "Sélecteur de canal", "sidebar_left.channel_navigator.goBackLabel": "Précédent", "sidebar_left.channel_navigator.jumpTo": "Aller à...", + "sidebar_left.sidebar_category_menu.muteCategory": "Catégorie en sourdine", "sidebar_left.sidebar_channel_menu.addMembers": "Ajouter Membres", "sidebar_left.sidebar_channel_menu.copyLink": "Copier le lien", "sidebar_left.sidebar_channel_menu.favoriteChannel": "Favoris", "sidebar_left.sidebar_channel_menu.leaveChannel": "Quitter le canal", "sidebar_left.sidebar_channel_menu.muteChannel": "Mettre le canal en sourdine", + "sidebar_left.sidebar_channel_menu.muteConversation": "Conversation en sourdine", "sidebar_left.sidebar_channel_menu.unmuteChannel": "Rétablir le son du canal", "sidebar_right_menu.console": "Console système", "sidebar_right_menu.flagged": "Messages marqués d'un indicateur", @@ -4015,9 +4029,12 @@ "user.settings.sidebar.groupChannelsTitle": "Groupement des canaux", "user.settings.sidebar.groupDesc": "Grouper les canaux par type, ou combiner tous les types dans une liste.", "user.settings.sidebar.icon": "Icône de paramètres de la barre latérale", + "user.settings.sidebar.limitVisibleGMsDMsTitle": "Nombre de messages personnels à afficher", "user.settings.sidebar.never": "Jamais", "user.settings.sidebar.off": "Désactivé", "user.settings.sidebar.on": "Activé", + "user.settings.sidebar.showUnreadsCategoryDesc": "Lorsqu'elle est activée, tous les canaux et messages personnels non lus sont regroupés dans la barre latérale.", + "user.settings.sidebar.showUnreadsCategoryTitle": "Regrouper les canaux non lus séparément", "user.settings.sidebar.sortAlpha": "Par ordre alphabétique", "user.settings.sidebar.sortAlphaShort": "triés par ordre alphabétique", "user.settings.sidebar.sortChannelsTitle": "Tri de canal", @@ -4026,7 +4043,7 @@ "user.settings.sidebar.sortRecentShort": "triés du plus récent au plus ancien", "user.settings.sidebar.title": "Paramètres de la barre latérale", "user.settings.sidebar.unreads": "Non lus groupés séparément", - "user.settings.sidebar.unreadsDesc": "Grouper les canaux non lus séparément jusqu'à ce qu'ils soient lus", + "user.settings.sidebar.unreadsDesc": "Grouper les canaux non lus séparément jusqu'à ce qu'ils soient lus.", "user.settings.sidebar.unreadsFavoritesShort": "Non lus et favoris groupés séparément", "user.settings.sidebar.unreadsShort": "Non lus groupés séparément", "user.settings.timezones.automatic": "Définir automatiquement", @@ -4062,9 +4079,11 @@ "user.settings.tokens.userAccessTokensNone": "Aucun jeton d'accès personnel.", "user_list.notFound": "Aucun utilisateur trouvé", "user_profile.account.editSettings": "Éditer les paramètres du compte", - "user_profile.account.localTime": "Heure locale : ", + "user_profile.account.localTime": "Heure locale", + "user_profile.account.post_was_created": "Ce message a été créé par une intégration de", "user_profile.add_user_to_channel": "Ajouter à un canal", "user_profile.add_user_to_channel.icon": "Icône d'ajout d'utilisateur à un canal", + "user_profile.custom_status": "Statut", "user_profile.send.dm": "Envoyer un message", "user_profile.send.dm.icon": "Icône d'envoi de message", "version_bar.new": "Une nouvelle version de Mattermost est disponible.", diff --git a/i18n/ja.json b/i18n/ja.json index e62792c8e280..d9ba5bb3d9ca 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -555,6 +555,9 @@ "admin.customization.gfycatApiSecretDescription": "APIキーに対してGfycatによって生成されたAPIシークレットです。空欄の場合、Gfycatにより提供されるデフォルトのAPIシークレットを使用します。", "admin.customization.iosAppDownloadLinkDesc": "iOSアプリのダウンロードリンクを追加してください。モバイル用ウェブブラウザーでサイトにアクセスしたユーザへ、アプリをダウンロードするか選択するページを表示します。空欄にした場合、そのページは表示されません。", "admin.customization.iosAppDownloadLinkTitle": "iOSアプリダウンロード用リンク:", + "admin.customization.restrictLinkPreviewsDesc": "カンマで区切られた上記のドメインに対しては、リンクプレビューやイメージリンクプレビューは表示されません。", + "admin.customization.restrictLinkPreviewsExample": "例:\"internal.mycompany.com, images.example.com\"", + "admin.customization.restrictLinkPreviewsTitle": "これらのドメインからのリンクプレビューを無効にします:", "admin.data_grid.empty": "アイテムが見付かりません", "admin.data_grid.loading": "読み込み中です", "admin.data_grid.paginatorCount": "全 {total, number} 中: {startCount, number} - {endCount, number}", @@ -568,6 +571,7 @@ "admin.data_retention.confirmChangesModal.title": "データ保持ポリシーを確認する", "admin.data_retention.createJob.help": "まもなくデータ保持削除処理を開始します。", "admin.data_retention.createJob.title": "今すぐ削除ジョブを実行する", + "admin.data_retention.customPolicies.addPolicy": "ポリシーを追加", "admin.data_retention.deletionJobStartTime.description": "毎日スケジュールされているデータ保持処理の開始時刻を設定します。システムを使用する人が少ない時間を選択してください。また、HH:MM形式の24時間表記を指定してください。", "admin.data_retention.deletionJobStartTime.example": "例: \"02:00\"", "admin.data_retention.deletionJobStartTime.title": "データ削除時間:", @@ -2568,6 +2572,8 @@ "commercial_support.download_support_packet": "サポートパケットをダウンロードする", "commercial_support.title": "商用サポート", "commercial_support.warning.banner": "サポートパケットをダウンロードする前に、[ログ設定](!/admin_console/environment/logging) から **ログをファイルに出力する** を **有効** にし、**ファイルログレベル** を **デバッグ** に設定してください。", + "confirm.notification_sent_to_admin.modal_body": "管理者に通知が送信されました。", + "confirm.notification_sent_to_admin.modal_title": "ありがとうございます!", "confirm_modal.cancel": "キャンセル", "convert_channel.cancel": "いいえ、キャンセルします", "convert_channel.confirm": "はい、非公開チャンネルに変更します", @@ -3135,7 +3141,11 @@ "intro_messages.creatorPrivate": "ここは非公開チャンネル {name} のトップです。{date}に{creator}によって作成されました。", "intro_messages.default": "**{display_name} へようこそ!**\n \n全員に見てほしいメッセージをここに投稿して下さい。チームに参加すると、全員が自動的にこのチャンネルのメンバーになります。", "intro_messages.group_message": "チームメイトとのグループメッセージの履歴の最初です。メッセージとそこで共有されているファイルは、この領域の外のユーザーからは見ることができません。", + "intro_messages.inviteGropusToChannel.button": "グループをこの非公開チャンネルに追加する", + "intro_messages.inviteMembersToChannel.button": "このチャンネルにメンバーを追加する", + "intro_messages.inviteMembersToPrivateChannel.button": "メンバーをこの非公開チャンネルに追加する", "intro_messages.inviteOthers": "他の人をこのチームに招待する", + "intro_messages.inviteOthersToWorkspace.button": "ワークスペースに他の人を招待する", "intro_messages.noCreator": "ここは {name} チャンネルのトップです。{date}に作成されました。", "intro_messages.noCreatorPrivate": "ここは非公開チャンネル {name} のトップです。{date}に作成されました。", "intro_messages.offTopic": "ここは{display_name}の始まりです。仕事とは関係のない会話のためのチャンネルです。", @@ -3477,6 +3487,7 @@ "next_steps_view.tips.exploreChannels": "チャンネルを探索する", "next_steps_view.tips.exploreChannels.button": "チャンネルを閲覧する", "next_steps_view.tips.exploreChannels.text": "ワークスペースのチャンネルを見てみるか、新たにチャンネルを作成してください。", + "next_steps_view.tips.manageWorkspace.button": "システムコンソールを開く", "next_steps_view.tips.viewMembers": "チームメンバーを閲覧する", "next_steps_view.tipsAndNextSteps": "コツと次のステップ", "next_steps_view.titles.completeProfile": "プロフィール入力を完了する", diff --git a/i18n/ko.json b/i18n/ko.json index 11c2e81ad7d8..28454b2b4e09 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -3,8 +3,8 @@ "about.cloudEdition": "클라우드", "about.copyright": "Copyright 2015 - {currentYear} Mattermost, Inc. 무단 전재와 무단 복제를 금합니다.", "about.database": "데이터베이스:", - "about.date": "빌드 일자:", - "about.dbversion": "데이터베이스 스키마 버전", + "about.date": "빌드 날짜:", + "about.dbversion": "데이터베이스 스키마 버전:", "about.enterpriseEditionLearn": "엔터프라이즈 에디션에 대한 자세한 정보 ", "about.enterpriseEditionSst": "엔터프라이즈에 적합한 신뢰도 높은 메시징", "about.enterpriseEditionSt": "보안 네트워크에 구축하는 현대적인 커뮤니케이션 플랫폼.", @@ -27,7 +27,7 @@ "accessibility.button.Search": "검색", "accessibility.button.attachment": "첨부", "accessibility.button.dialog": "{dialogName} 다이얼로그", - "accessibility.sections.centerContent": "메시지 목록 주요 지역", + "accessibility.sections.centerContent": "메시지 목록 주요 영역", "accessibility.sections.centerFooter": "메시지 입력 영역", "accessibility.sections.channelHeader": "채널 헤더 영역", "accessibility.sections.lhsHeader": "팀 메뉴 영역", @@ -42,7 +42,7 @@ "accessibility.sidebar.types.unread": "읽지 않음", "activity_log.activeSessions": "활성 세션", "activity_log.browser": "브라우저: {browser}", - "activity_log.firstTime": "첫 활동 시간: {date}, {time}", + "activity_log.firstTime": "첫 활동 시각: {date}, {time}", "activity_log.lastActivity": "최근 활동: {date}, {time}", "activity_log.logout": "로그아웃", "activity_log.moreInfo": "상세 정보", @@ -61,7 +61,7 @@ "add_command.autocompleteDescription": "자동완성 설명", "add_command.autocompleteDescription.help": "(선택) 자동완성 목록에서 보여질 부가적인 설명을 입력하세요.", "add_command.autocompleteDescription.placeholder": "예시: \"환자 기록에 대한 검색결과를 보여줍니다\"", - "add_command.autocompleteHint": "자동완성 힌트", + "add_command.autocompleteHint": "자동완성 제안", "add_command.autocompleteHint.help": "(선택) 슬래시 명령어의 매개변수를 지정합니다.", "add_command.autocompleteHint.placeholder": "예시: [환자 이름]", "add_command.cancel": "취소", @@ -78,7 +78,7 @@ "add_command.method.help": "Mattermost가 애플리케이션과 통신하기 위해 전송하는 POST 혹은 GET 요청의 유형을 지정합니다.", "add_command.method.post": "POST", "add_command.save": "저장", - "add_command.saving": "저장중...", + "add_command.saving": "저장 중...", "add_command.token": "**토큰**: {token}", "add_command.trigger": "명령어 트리거 단어", "add_command.trigger.help": "내장 명령어가 아닌 트리거 단어를 지정합니다. 공백을 포함하거나, 슬래시 문자열로 시작해서는 안됩니다.", @@ -86,7 +86,7 @@ "add_command.trigger.helpReserved": "사용할 수 없음: {link}", "add_command.trigger.helpReservedLinkText": "내장된 슬래시(/) 명령어를 확인해주세요", "add_command.trigger.placeholder": "슬래시를 포함하지 않는 명령 트리거. 예: \"hello\"", - "add_command.triggerInvalidLength": "단어가 {min} 글자 이상, {max} 글자 이하여야 합니다.", + "add_command.triggerInvalidLength": "트리거 단어는 {min} 글자 이상, {max} 글자 이하여야 합니다.", "add_command.triggerInvalidSlash": "단어 앞에 슬래시(/)를 사용할 수 없습니다.", "add_command.triggerInvalidSpace": "단어에 공백을 포함할 수 없습니다.", "add_command.triggerRequired": "단어가 필요합니다.", @@ -242,6 +242,7 @@ "admin.billing.history.transactions": "트랜잭션", "admin.billing.payment_info.add": "신용 카드 추가", "admin.billing.payment_info.billingAddress": "청구 주소", + "admin.billing.payment_info.cardBrandAndDigits": "{digits}로 끝나는 {brand}카드", "admin.billing.payment_info.cardExpiry": "만료일 {month}/{year}", "admin.billing.payment_info.creditCardAboutToExpire": "신용 카드가 곧 만료됩니다", "admin.billing.payment_info.creditCardAboutToExpire.description": "서비스 중단을 방지하려면 결제 정보를 업데이트하십시오.", @@ -276,8 +277,8 @@ "admin.billing.subscription.paymentFailed": "결제 실패. 다시 시도하거나 지원팀에 문의하십시오.", "admin.billing.subscription.paymentVerificationFailed": "죄송합니다. 결제 확인에 실패했습니다", "admin.billing.subscription.perUserPerMonth": " /user/month", - "admin.billing.subscription.planDetails.currentPlan": "현제 플랜", - "admin.billing.subscription.planDetails.endDate": "종료 일: ", + "admin.billing.subscription.planDetails.currentPlan": "현재 플랜", + "admin.billing.subscription.planDetails.endDate": "종료일: ", "admin.billing.subscription.planDetails.features.10GBstoragePerUser": "사용자 당 10GB 저장 용량", "admin.billing.subscription.planDetails.features.99uptime": "99.0% 가동 시간", "admin.billing.subscription.planDetails.features.guestAccounts": "게스트 계정", @@ -289,6 +290,7 @@ "admin.billing.subscription.planDetails.numberOfSeats": "{numberOfSeats} seats", "admin.billing.subscription.planDetails.numberOfSeatsRegistered": "({userCount} 현재 가입자)", "admin.billing.subscription.planDetails.perUserPerMonth": "/user/month", + "admin.billing.subscription.planDetails.planDetailsName.freeUpTo": "사용자 {aboveUserLimit}명까지 무료.", "admin.billing.subscription.planDetails.prolongedOverages": "오랜기간 초과되면 추가요금이 발생할 수 있습니다.", "admin.billing.subscription.planDetails.seatCountOverages": "인원 초과", "admin.billing.subscription.planDetails.startDate": "시작일: ", @@ -548,6 +550,8 @@ "admin.customization.gfycatApiSecretDescription": "API 키를 위해 Gfycat에서 생성한 API 시크릿입니다. 비어있으면, Gfycat에서 제공한 기본 API 시크릿을 사용합니다.", "admin.customization.iosAppDownloadLinkDesc": "iOS 앱을 다운로드할 수 있는 주소를 추가하세요. 모바일 웹 브라우저를 통해 사이트에 접속하는 사용자에게는 앱 다운로드 옵션을 제공하는 페이지가 표시됩니다. 사용하지 않으려면 이 항목을 비워두세요", "admin.customization.iosAppDownloadLinkTitle": "iOS 앱 다운로드 링크:", + "admin.customization.restrictLinkPreviewsExample": "예시: \"internal.mycompany.com, images.example.com\"", + "admin.customization.restrictLinkPreviewsTitle": "다음 도메인에서 미리 보기 방지:", "admin.data_grid.empty": "사용자를 찾을 수 없습니다 :(", "admin.data_grid.loading": "불러오는 중", "admin.data_grid.paginatorCount": "{total, number} 의 {startCount, number} - {endCount, number}", @@ -561,6 +565,8 @@ "admin.data_retention.confirmChangesModal.title": "데이터 보존 정책 확인", "admin.data_retention.createJob.help": "데이터 보존 삭제 작업을 즉시 시작합니다.", "admin.data_retention.createJob.title": "삭제 작업 지금 실행", + "admin.data_retention.customPolicies.addPolicy": "정책 추가", + "admin.data_retention.customPolicies.subTitle": "특정 팀과 채널의 메세지 보관 기간을 지정합니다.", "admin.data_retention.deletionJobStartTime.description": "일일 데이터 보존 스케쥴의 시작 시간을 설정하십시오. 시스템 사용자가 적은 시간대를 선택하십시오. HH:MM 형식의 24시간 타임 스탬프 여야합니다.", "admin.data_retention.deletionJobStartTime.example": "예시: \"02:00\"", "admin.data_retention.deletionJobStartTime.title": "데이터 삭제 시간 :", @@ -1326,7 +1332,10 @@ "admin.permissions.roles.all_users.name": "모든 멤버", "admin.permissions.roles.channel_admin.name": "채널 관리자", "admin.permissions.roles.channel_user.name": "채널 사용자", + "admin.permissions.roles.edit": "수정", + "admin.permissions.roles.system_admin.description": "전체 수정 권한 부여", "admin.permissions.roles.system_admin.name": "시스템 관리자", + "admin.permissions.roles.system_manager.name": "시스템 매니저", "admin.permissions.roles.system_user.name": "시스템 사용자", "admin.permissions.roles.team_admin.name": "팀 관리자", "admin.permissions.roles.team_user.name": "팀 사용자", diff --git a/i18n/nl.json b/i18n/nl.json index b07326701978..1c6a25658fb1 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -3914,6 +3914,7 @@ "signup_user_completed.usernameLength": "Gebruikersnamen moeten beginnen met een kleine letter en tussen {min}-{max} tekens lang zijn. Je kan kleine letters, cijfers, punten, streepjes en liggende streepjes gebruiken.", "signup_user_completed.validEmail": "Vul een geldig e-mail adres in", "signup_user_completed.whatis": "Wat is uw e-mail adres?", + "someting.string": "standaartText", "status_dropdown.custom_status.tooltip_clear": "Wissen", "status_dropdown.menuAriaLabel": "Zet status", "status_dropdown.set_away": "Afwezig", diff --git a/i18n/ru.json b/i18n/ru.json index 4ffca91f18af..8460908658b7 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -32,6 +32,7 @@ "accessibility.sections.channelHeader": "область заголовка канала", "accessibility.sections.lhsHeader": "область меню команды", "accessibility.sections.lhsList": "область боковой панели канала", + "accessibility.sections.lhsNavigator": "область навигатора канала", "accessibility.sections.rhs": "дополнительная область {regionTitle}", "accessibility.sections.rhsContent": "дополнительная область деталей сообщения", "accessibility.sections.rhsFooter": "область ввода ответа", @@ -202,6 +203,7 @@ "admin.authentication.ldap": "AD/LDAP", "admin.authentication.mfa": "Многофакторная аутентификация", "admin.authentication.oauth": "OAuth 2.0", + "admin.authentication.openid": "OpenID Connect", "admin.authentication.saml": "SAML 2.0", "admin.authentication.signup": "Регистрация", "admin.banner.heading": "Заметка:", @@ -239,6 +241,7 @@ "admin.billing.history.title": "История счетов", "admin.billing.history.total": "Всего", "admin.billing.history.transactions": "Транзакции", + "admin.billing.history.usersAndRates": "{fullUsers} пользователи на полную ставку, {partialUsers} пользователи с частичной оплатой", "admin.billing.payment_info.add": "Добавить кредитную карту", "admin.billing.payment_info.billingAddress": "Адрес для выставления счета", "admin.billing.payment_info.cardBrandAndDigits": "{brand} истекает {digits}", @@ -256,6 +259,9 @@ "admin.billing.payment_info_edit.save": "Сохранить кредитную карту", "admin.billing.payment_info_edit.serverError": "Что-то пошло не так при сохранении платежной информации", "admin.billing.payment_info_edit.title": "Изменить платежную информацию", + "admin.billing.subscription.cancelSubscriptionSection.contactUs": "Связаться с нами", + "admin.billing.subscription.cancelSubscriptionSection.description": "Сейчас удаление рабочего пространства может быть выполнено только с помощью представителя службы поддержки клиентов.", + "admin.billing.subscription.cancelSubscriptionSection.title": "Отменить подписку", "admin.billing.subscription.creditCardExpired": "Срок действия вашей карты истек. Обновите платежную информацию.", "admin.billing.subscription.creditCardHasExpired": "Срок действия карты истек", "admin.billing.subscription.creditCardHasExpired.description.avoidAnyDisruption": " во избежание проблем.", @@ -268,6 +274,8 @@ "admin.billing.subscription.mattermostCloud": "Mattermost Cloud", "admin.billing.subscription.mostRecentPaymentFailed": "Последний платеж не прошел", "admin.billing.subscription.nextBillingDate": "Начиная с {date}, с вас будет взиматься плата в зависимости от количества включенных пользователей", + "admin.billing.subscription.otherBillingOption": "Нужны другие варианты выставления счетов?", + "admin.billing.subscription.payamentBegins": "Оплата начинается: {beginDate}", "admin.billing.subscription.paymentFailed": "Платеж не прошел. Пожалуйста, попробуйте еще раз или обратитесь в службу поддержки.", "admin.billing.subscription.paymentVerificationFailed": "Извините, проверка платежа не прошла", "admin.billing.subscription.perUserPerMonth": " /пользователь/месяц", @@ -284,23 +292,45 @@ "admin.billing.subscription.planDetails.numberOfSeats": "{numberOfSeats} мест", "admin.billing.subscription.planDetails.numberOfSeatsRegistered": "({userCount} сейчас зарегистрировано)", "admin.billing.subscription.planDetails.perUserPerMonth": "/пользователь/месяц", + "admin.billing.subscription.planDetails.planDetailsName.freeForXOrMoreUsers": "/пользователь/месяц для {aboveUserLimit} или более пользователей.", + "admin.billing.subscription.planDetails.planDetailsName.freeUpTo": "Бесплатно для {aboveUserLimit} пользователей.", "admin.billing.subscription.planDetails.prolongedOverages": "Продолжительное превышение может привести к дополнительным расходам.", "admin.billing.subscription.planDetails.seatCountOverages": "Количество мест превышено", "admin.billing.subscription.planDetails.startDate": "Дата начала: ", + "admin.billing.subscription.planDetails.tiers.free": "Бесплатно", "admin.billing.subscription.planDetails.upToXUsers": "до {userLimit} пользователей", "admin.billing.subscription.planDetails.userCount": "{userCount} пользователей", "admin.billing.subscription.planDetails.userCountWithLimit": "{userCount} / {userLimit} пользователей", + "admin.billing.subscription.privateCloudCard.contactSales": "Контакты отдела продаж", "admin.billing.subscription.privateCloudCard.contactSupport": "Связаться со службой поддержки", + "admin.billing.subscription.privateCloudCard.description": "Если вам нужно программное обеспечение с выделенной архитектурой с одним арендатором, Mattermost Private Cloud (Beta) является решением для совместной работы с высоким уровнем доверия.", "admin.billing.subscription.privateCloudCard.title": "Ищете частное облако с высоким уровнем доверия?", "admin.billing.subscription.questions": "Вопросы?", "admin.billing.subscription.stateprovince": "Область/Провинция", "admin.billing.subscription.title": "Подписки", + "admin.billing.subscription.updatePaymentInfo": "Обновить информацию об оплате", "admin.billing.subscription.upgrade": "Обновить", "admin.billing.subscription.upgradeCloudSubscription": "Расширьте подписку на Mattermost Cloud", "admin.billing.subscription.upgradeMattermostCloud.description": "Уровень бесплатного использования **ограничен до 10 пользователей.** Получите доступ к большему количеству пользователей, команд и другим замечательным функциям", "admin.billing.subscription.upgradeMattermostCloud.title": "Нужно больше пользователей?", "admin.billing.subscription.upgradeMattermostCloud.upgradeButton": "Улучшить Mattermost Cloud", + "admin.billing.subscription.upgradedSuccess": "Отлично! Вы обновились", + "admin.billing.subscription.verifyPaymentInformation": "Проверка платежных данных", + "admin.billing.subscriptions.billing_summary.lastInvoice.downloadInvoice": "Скачать счёт", + "admin.billing.subscriptions.billing_summary.lastInvoice.failed": "Неудача", + "admin.billing.subscriptions.billing_summary.lastInvoice.paid": "Оплачено", + "admin.billing.subscriptions.billing_summary.lastInvoice.partialCharges": "Частичные оплаты", + "admin.billing.subscriptions.billing_summary.lastInvoice.pending": "В ожидании", + "admin.billing.subscriptions.billing_summary.lastInvoice.seeBillingHistory": "См. историю выставления счетов", + "admin.billing.subscriptions.billing_summary.lastInvoice.taxes": "Налоги", + "admin.billing.subscriptions.billing_summary.lastInvoice.title": "Последний счёт", + "admin.billing.subscriptions.billing_summary.lastInvoice.total": "Всего", + "admin.billing.subscriptions.billing_summary.lastInvoice.userCount": " x {users} пользователей", + "admin.billing.subscriptions.billing_summary.lastInvoice.userCountPartial": "{users} пользователей", + "admin.billing.subscriptions.billing_summary.lastInvoice.whatArePartialCharges": "Что такое частичные оплаты?", + "admin.billing.subscriptions.billing_summary.lastInvoice.whatArePartialCharges.message": "Пользователи, которые не были подключены в течение всего месяца, оплачиваются пропорционально по месячному тарифу.", "admin.billing.subscriptions.billing_summary.noBillingHistory.description": "В будущем здесь будет отображаться ваша последняя сводка счета.", + "admin.billing.subscriptions.billing_summary.noBillingHistory.link": "Посмотреть, как работает биллинг", "admin.billing.subscriptions.billing_summary.noBillingHistory.title": "Истории счетов пока нет", "admin.bleve.bulkIndexingTitle": "Массовая индексация:", "admin.bleve.createJob.help": "Все пользователи, каналы и сообщения в базе данных будут проиндексированы, начиная с самых старых. Bleve будет доступен во время индексирования, однако результаты поисковых запросов могут быть неполны.", @@ -386,7 +416,7 @@ "admin.channel_settings.channel_moderation.postReactionsDescMembers": "Способность участников создавать реакции на сообщения.", "admin.channel_settings.channel_moderation.subtitle": "Управление действиями доступно участникам и гостям канала.", "admin.channel_settings.channel_moderation.subtitleMembers": "Управление действиями доступно участникам канала.", - "admin.channel_settings.channel_moderation.title": "Модерация канала (Бета)", + "admin.channel_settings.channel_moderation.title": "Модерация канала", "admin.channel_settings.channel_row.configure": "Изменить", "admin.channel_settings.description": "Управление настройками канала.", "admin.channel_settings.groupsPageTitle": "Каналы {siteName}", @@ -396,6 +426,8 @@ "admin.cluster.ClusterNameEx": "Например: \"Production\" или \"Staging\"", "admin.cluster.EnableExperimentalGossipEncryption": "Включить экспериментальное шифрование Gossip:", "admin.cluster.EnableExperimentalGossipEncryptionDesc": "При значении 'да' все сообщения по протоколу gossip будут зашифрованы.", + "admin.cluster.EnableGossipCompression": "Включить сжатие Gossip:", + "admin.cluster.EnableGossipCompressionDesc": "Если true, то все данные, передаваемые по протоколу Gossip, будут сжаты. Рекомендуется держать этот флаг отключенным.", "admin.cluster.GossipPort": "Gossip порт:", "admin.cluster.GossipPortDesc": "Порт, используемый для протокола gossip. На этом порту должны быть разрешены как UDP, так и TCP.", "admin.cluster.GossipPortEx": "Например: \"8074\"", @@ -405,7 +437,7 @@ "admin.cluster.StreamingPort": "Потоковый порт:", "admin.cluster.StreamingPortDesc": "Порт, используемый для потоковой передачи данных между серверами.", "admin.cluster.StreamingPortEx": "Например: \"8075\"", - "admin.cluster.UseExperimentalGossip": "Использовать экспериментальный gossip:", + "admin.cluster.UseExperimentalGossip": "Использовать протокол Gossip:", "admin.cluster.UseExperimentalGossipDesc": "При значении \"да\" сервер попытается связаться по протоколу gossip через порт gossip. При значении \"нет\" сервер будет пытаться установить связь через потоковый порт. При значении \"нет\" порт и протокол gossip все еще используются для определения работоспособности кластера.", "admin.cluster.UseIpAddress": "Использовать IP-адрес:", "admin.cluster.UseIpAddressDesc": "При значении \"да\" кластер будет пытаться установить связь через IP-адрес вместо использования имени хоста.", @@ -523,6 +555,9 @@ "admin.customization.gfycatApiSecretDescription": "Секрет API, сгенерированный Gfycat для вашего ключа API. Если поле пустое, используется секрет API по умолчанию, предоставленный Gfycat.", "admin.customization.iosAppDownloadLinkDesc": "Добавляет ссылку для скачивания приложения для IOS. Пользователям, которые посещают сайт через мобильный браузер, на специальной странице будет предложена возможность скачать приложение. Оставьте это поле пустым, чтобы предотвратить появление этой страницы.", "admin.customization.iosAppDownloadLinkTitle": "Ссылка на страницу загрузки приложения для iOS:", + "admin.customization.restrictLinkPreviewsDesc": "Предварительный просмотр ссылок и просмотр ссылок на изображения не будут показаны для вышеприведенного списка доменов, разделенных запятыми.", + "admin.customization.restrictLinkPreviewsExample": "Например: \"internal.mycompany.com, images.example.com\"", + "admin.customization.restrictLinkPreviewsTitle": "Отключить просмотр ссылок с этих доменов:", "admin.data_grid.empty": "Ничего не найдено", "admin.data_grid.loading": "Загрузка", "admin.data_grid.paginatorCount": "{startCount, number} - {endCount, number} из {total, number}", @@ -536,6 +571,9 @@ "admin.data_retention.confirmChangesModal.title": "Подтвердите политику хранения данных", "admin.data_retention.createJob.help": "Инициирует задание по удалению данных немедленно.", "admin.data_retention.createJob.title": "Запустить задание по удалению сейчас", + "admin.data_retention.customPolicies.addPolicy": "Добавить политику", + "admin.data_retention.customPolicies.subTitle": "Настроить, как долго определённые команды и каналы будут хранить сообщения.", + "admin.data_retention.customPolicies.title": "Настраиваемая политика удержания", "admin.data_retention.deletionJobStartTime.description": "Установите время начала ежедневного задания сохранения данных. Выберите время, когда наименьшее число людей используют вашу систему. Время должно быть в 24-часовом формате ЧЧ:ММ.", "admin.data_retention.deletionJobStartTime.example": "Например: \"02:00\"", "admin.data_retention.deletionJobStartTime.title": "Время удаления данных:", @@ -697,6 +735,8 @@ "admin.experimental.emailSettingsLoginButtonTextColor.title": "Цвет текста кнопки входа в систему:", "admin.experimental.enableChannelViewedMessages.desc": "Этот параметр определяет, будут ли отправляться события `channel_viewed` WebSocket, которые синхронизируют непрочитанные уведомления между клиентами и устройствами. Отключение параметра в больших развертываниях может повысить производительность сервера.", "admin.experimental.enableChannelViewedMessages.title": "Включить WebSocket сообщения \"Channel Viewed\":", + "admin.experimental.enableLegacySidebar.desc": "Когда эта функция включена, пользователи не могут получить доступ к новым функциям боковой панели, включая пользовательские, складывающиеся категории и фильтрацию непрочитанных каналов. Мы рекомендуем включать старую боковую панель только в том случае, если у пользователей возникли проблемы с изменениями или ошибки.", + "admin.experimental.enableLegacySidebar.title": "Включить режим \"Старая боковая панель\"", "admin.experimental.enablePreviewFeatures.desc": "При значении \"да\" предрелизные функции можно включить из **Настройки учетной записи > Дополнительно > Ознакомление с предрелизными функциями**. При значении \"нет\" отключает и скрывает предрелизные функции из **Настройки учетной записи > Дополнительно > Ознакомление с предрелизными функциями**.", "admin.experimental.enablePreviewFeatures.title": "Включить предрелизные функции:", "admin.experimental.enableThemeSelection.desc": "Включает вкладку **Вид > Тема** в настройках учетной записи, чтобы пользователи могли выбирать свою тему.", @@ -707,9 +747,9 @@ "admin.experimental.enableUserDeactivation.title": "Включить деактивацию аккаунта:", "admin.experimental.enableUserTypingMessages.desc": "Этот параметр определяет, отображаются ли сообщения «пользователь печатает ...» под окном сообщений. Отключение параметра в больших развертываниях может повысить производительность сервера.", "admin.experimental.enableUserTypingMessages.title": "Включить сообщения \"пользователь печатает...\":", - "admin.experimental.enableXToLeaveChannelsFromLHS.desc": "При значении \"да\" пользователи могут покинуть публичные и частные каналы, щелкнув «x» рядом с названием канала. При значении \"нет\" пользователи должны использовать функцию **Покинуть канал** в меню каналов, чтобы покинуть каналы.", + "admin.experimental.enableXToLeaveChannelsFromLHS.desc": "При значении \"да\" пользователи могут покинуть публичные и частные каналы, щёлкнув «x» рядом с названием канала. При значении \"нет\" пользователи должны использовать функцию **Покинуть канал** в меню каналов, чтобы покинуть каналы.", "admin.experimental.enableXToLeaveChannelsFromLHS.title": "Разрешить нажимать X, чтобы покинуть канал с левой боковой панели:", - "admin.experimental.experimentalChannelOrganization.desc": "Включает параметры организации боковой панели канала в **Учетная запись>Боковая панель> Группировка и сортировка каналов**, включая параметры группировки непрочитанных каналов, сортировки каналов по последним публикациям и объединения всех типов каналов в один список. Эти параметры недоступны, если **Учетная запись>Боковая панель>Экспериментальные функции боковой панели** включены.", + "admin.experimental.experimentalChannelOrganization.desc": "Включает параметры организации боковой панели канала в **Учётная запись>Боковая панель> Группировка и сортировка каналов**, включая параметры группировки непрочитанных каналов, сортировки каналов по последним публикациям и объединения всех типов каналов в один список. Эти параметры недоступны, если **Учётная запись>Боковая панель>Экспериментальные функции боковой панели** включены.", "admin.experimental.experimentalChannelOrganization.title": "Группировка и сортировка каналов", "admin.experimental.experimentalEnableAuthenticationTransfer.desc": "При значении \"да\" пользователи могут изменить свой метод входа на любой, который включен на сервере, либо через настройки учетной записи, либо через API. При значении \"нет\" пользователи не могут изменять свой метод входа, независимо от того, какие параметры аутентификации включены.", "admin.experimental.experimentalEnableAuthenticationTransfer.title": "Разрешить передачу аутентификации:", @@ -755,6 +795,10 @@ "admin.experimental.userStatusAwayTimeout.example": "Например: \"300\"", "admin.experimental.userStatusAwayTimeout.title": "Тайм-аут статуса пользователя \"Отошёл\":", "admin.false": "нет", + "admin.feature_flags.flag": "Отметить", + "admin.feature_flags.flag_value": "Значение", + "admin.feature_flags.introBanner": "Значения флагов функций, отображаемых здесь, показывают состояние функций, включенных на данном сервере. Значения здесь предназначены только для отладки командой поддержки Mattermost.", + "admin.feature_flags.title": "Флаги функций", "admin.field_names.allowBannerDismissal": "Включить возможность скрытия баннера", "admin.field_names.bannerColor": "Цвет баннера", "admin.field_names.bannerText": "Текст баннера", @@ -803,7 +847,7 @@ "admin.general.localization.serverLocaleDescription": "Язык системных сообщений по умолчанию. Требует перезагрузки сервера прежде чем вступит в силу.", "admin.general.localization.serverLocaleTitle": "Язык сервера по умолчанию:", "admin.general.log": "Ведение журнала", - "admin.gitlab.EnableMarkdownDesc": "1. Войдите в свою учетную запись на GitLab и пройдите в Настройки профиля -> Приложения.\n2. Введите перенаправляющие URI-адреса \"<url-вашего-mattermost>/login/gitlab/complete\" (пример: http://localhost:8065/login/gitlab/complete) и \"<url-вашего-mattermost>/signup/gitlab/complete\".\n3. Затем используйте поля \"Секретный ключ приложения\" и \"Идентификатор приложения\" из GitLab для заполнения опций ниже.\n4. Заполните URL-адреса конечных точек ниже.", + "admin.gitlab.EnableMarkdownDesc": "1. Войдите в свою учётную запись на GitLab и пройдите в Profile Settings -> Applications.\n2. Введите Redirect URI-адреса \"/login/gitlab/complete\" (пример: http://localhost:8065/login/gitlab/complete) и \"/signup/gitlab/complete\".\n3. Затем используйте поля \"Application Secret Key\" и \"Application ID\" из GitLab для заполнения опций ниже.\n4. Заполните Endpoint URL-адреса ниже.", "admin.gitlab.authTitle": "Конечная точка авторизации:", "admin.gitlab.clientIdDescription": "Получите это значение с помощью вышеуказанных инструкций для входа в GitLab.", "admin.gitlab.clientIdExample": "Например: \"jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY\"", @@ -811,6 +855,7 @@ "admin.gitlab.clientSecretDescription": "Получите это значение с помощью вышеуказанных инструкций для входа в GitLab.", "admin.gitlab.clientSecretExample": "Например: \"jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY\"", "admin.gitlab.clientSecretTitle": "Секретный ключ приложения:", + "admin.gitlab.discoveryEndpointDesc": "Discovery Document URL для OpenID Connect с GitLab.", "admin.gitlab.enableDescription": "При значении \"да\" Mattermost позволяет создавать команды и регистрировать учетные записи с помощью GitLab OAuth.\n \n1. Войдите в свою учетную запись на GitLab и пройдите в Настройки профиля -> Приложения.\n2. Введите перенаправляющие URI-адреса \"''/login/gitlab/complete\" (пример: http://localhost:8065/login/gitlab/complete) и \"''/signup/gitlab/complete\".\n3. Затем используйте поля \"Секретный ключ приложения\" и \"Идентификатор приложения\" из GitLab для заполнения опций ниже.\n4. Заполните URL-адреса конечных точек ниже.", "admin.gitlab.enableTitle": "Включить аутентификацию через GitLab: ", "admin.gitlab.siteUrl": "URL сайта GitLab: ", @@ -826,6 +871,7 @@ "admin.google.clientSecretDescription": "Секрет клиента, полученный при регистрации вашего приложения в Google.", "admin.google.clientSecretExample": "Например: \"H8sz0Az-dDs2p15-7QzD231\"", "admin.google.clientSecretTitle": "Клиентский ключ:", + "admin.google.discoveryEndpointDesc": "Discovery Document URL для OpenID Connect с Google.", "admin.google.tokenTitle": "Адрес выдачи токена:", "admin.google.userTitle": "Конечная точка API пользователя:", "admin.group_settings.filters.isConfigured": "Настроен", @@ -3250,6 +3296,8 @@ "pending_post_actions.retry": "Повторить", "permalink.error.access": "Постоянная ссылка принадлежит к удалённому сообщению или каналу, к которому у вас нет доступа.", "permalink.error.title": "Сообщение не найдено", + "permalink.show_dialog_warn.join": "Присоединиться", + "permalink.show_dialog_warn.title": "Присоединиться к приватному каналу", "post.ariaLabel.attachment": ", 1 вложение", "post.ariaLabel.attachmentMultiple": ", {attachmentCount} вложений", "post.ariaLabel.message": "В {time} {date}, {authorName} написал, {message}", diff --git a/i18n/sv.json b/i18n/sv.json index b683d7e5dca0..e67f85e11cae 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -2570,7 +2570,7 @@ "combined_system_message.removed_from_team.one_you": "Du **togs bort från teamet**.", "combined_system_message.removed_from_team.two": "{firstUser} och {secondUser} **togs bort från teamet**.", "combined_system_message.you": "Du", - "commercial_support.description": "Om du upplever problem, [skapa ett supportärende.](!https://support.mattermost.com/hc/en-us/requests/new)\n \n**Ladda ner Systemdetaljer**\n \nVi rekommenderar att du laddar ner systemdetaljer om din Mattermostmiljö som stöd i felsökningen. När du laddat ner informationen, bifoga det till ditt ärende för att ge kundsupportteamet tillgång till informationen.", + "commercial_support.description": "Om du upplever problem, [skapa ett supportärende.](!{supportLink})\n \n**Ladda ner Systemdetaljer**\n \nVi rekommenderar att du laddar ner systemdetaljer om din Mattermostmiljö som stöd i felsökningen. När du laddat ner informationen, bifoga det till ditt ärende för att ge kundsupportteamet tillgång till informationen.", "commercial_support.download_support_packet": "Ladda ner Systemdetaljer", "commercial_support.title": "Kommersiell support", "commercial_support.warning.banner": "Innan du laddar ner supportdetaljer, sätt **Spara loggar till fil** till **På** och sätt **Fil-loggnivå** till **DEBUG** [här](!/admin_console/environment/logging).", @@ -3914,6 +3914,7 @@ "signup_user_completed.usernameLength": "Användarnamn måste börja med en gemen och kan var {min}-{max} tecken långt. Du kan använda gemener, siffror, punkt, streck och understreck.", "signup_user_completed.validEmail": "Ange en giltig e-postadress", "signup_user_completed.whatis": "Vad har du för e-postadress?", + "someting.string": "standardtext", "status_dropdown.custom_status.tooltip_clear": "Nollställ", "status_dropdown.menuAriaLabel": "ange status", "status_dropdown.set_away": "Tillfälligt borta", diff --git a/i18n/tr.json b/i18n/tr.json index 3555becb7463..6b8e044339ee 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -3914,6 +3914,7 @@ "signup_user_completed.usernameLength": "Kullanıcı adları bir küçük harf ile başlamalı ve {min} ile {max} karakter arasında bir uzunluğa sahip olmalıdır. Küçük harfleri, rakamları, nokta, tire ve alt çizgi karakterlerini kullanabilirsiniz.", "signup_user_completed.validEmail": "Lütfen geçerli bir e-posta adresi yazın", "signup_user_completed.whatis": "E-posta adresiniz nedir?", + "someting.string": "varsayilanDizge", "status_dropdown.custom_status.tooltip_clear": "Temizle", "status_dropdown.menuAriaLabel": "durumu ayarla", "status_dropdown.set_away": "Uzakta", diff --git a/i18n/zh-CN.json b/i18n/zh-CN.json index b0db8f25ebd1..fb7554eeef61 100644 --- a/i18n/zh-CN.json +++ b/i18n/zh-CN.json @@ -3102,7 +3102,7 @@ "intro_messages.DM": "这是您和{teammate}私信记录的开端。\n此区域外的人不能看到这里共享的私信和文件。", "intro_messages.GM": "这是您和{names}团体消息的起端。\n此区域外的人不能看到这里共享的消息和文件。", "intro_messages.addGroupsToTeam": "添加其他组到此团队", - "intro_messages.anyMember": " 任何成员可以加入和查看这个频道。", + "intro_messages.anyMember": " 任何成员可以加入与查看该频道。", "intro_messages.beginning": "{name} 的开端", "intro_messages.creator": "这是{name}频道的开端,由{creator}于{date}建立。", "intro_messages.creatorPrivate": "这是{name}私有频道的开端,由{creator}于{date}建立。", diff --git a/i18n/zh-TW.json b/i18n/zh-TW.json index 6b69bc4d2c63..70a6d4886abd 100644 --- a/i18n/zh-TW.json +++ b/i18n/zh-TW.json @@ -32,6 +32,7 @@ "accessibility.sections.channelHeader": "頻道標題區域", "accessibility.sections.lhsHeader": "團隊選單區域", "accessibility.sections.lhsList": "頻道側邊欄區域", + "accessibility.sections.lhsNavigator": "頻道導航器區域", "accessibility.sections.rhs": "{regionTitle}補助區域", "accessibility.sections.rhsContent": "訊息詳細資訊補助區域", "accessibility.sections.rhsFooter": "回應輸入區域", From 5db00d65d820121aabf469c84b63fa2a81af6000 Mon Sep 17 00:00:00 2001 From: Elisabeth Kulzer Date: Mon, 29 Mar 2021 12:01:03 +0200 Subject: [PATCH 02/28] Make webapp build faster. (#7779) --- .gitlab-ci.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0cde21c2871d..e484c1fe0429 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,7 @@ stages: - test - # These need to have separate stages, otherwise artifacts would overwrite each other - - te-build - - te-s3 - - ee-build - - ee-s3 - - create-vars + - build + - s3 - trigger variables: @@ -18,7 +14,7 @@ include: file: private.yml empty: - stage: create-vars + stage: test script: - echo "empty" From 0fccbe4d782b04e449bdfce7ddfe6c363cad9ba1 Mon Sep 17 00:00:00 2001 From: ctlaltdieliet Date: Mon, 29 Mar 2021 22:01:28 +0200 Subject: [PATCH 03/28] Removing Beta from sv and bg (#7764) * Removing Beta from sv and bg * Updated the snapshots Co-authored-by: Tom De Moor Co-authored-by: Mattermod --- .../__snapshots__/schema_admin_settings.test.jsx.snap | 8 ++++---- i18n/i18n.jsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap b/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap index 723871866b82..7f8b7e0f4e53 100644 --- a/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap +++ b/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap @@ -331,7 +331,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with }, Object { "order": 9, - "text": "Svenska (Beta)", + "text": "Svenska", "value": "sv", }, Object { @@ -341,7 +341,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with }, Object { "order": 11, - "text": "Български (Beta)", + "text": "Български", "value": "bg", }, Object { @@ -447,7 +447,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with }, Object { "order": 9, - "text": "Svenska (Beta)", + "text": "Svenska", "value": "sv", }, Object { @@ -457,7 +457,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with }, Object { "order": 11, - "text": "Български (Beta)", + "text": "Български", "value": "bg", }, Object { diff --git a/i18n/i18n.jsx b/i18n/i18n.jsx index 7974be758ff3..b2bc1a74e4c7 100644 --- a/i18n/i18n.jsx +++ b/i18n/i18n.jsx @@ -82,7 +82,7 @@ const languages = { }, sv: { value: 'sv', - name: 'Svenska (Beta)', + name: 'Svenska', order: 9, url: sv, }, @@ -94,7 +94,7 @@ const languages = { }, bg: { value: 'bg', - name: 'Български (Beta)', + name: 'Български', order: 11, url: bg, }, From ab6be48665de4818707050fcf2056295125f27f2 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Mon, 29 Mar 2021 16:20:52 -0400 Subject: [PATCH 04/28] MM-34055 Re-add border to Markdown preview (#7747) --- sass/components/_post.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sass/components/_post.scss b/sass/components/_post.scss index 6703dd743ae0..016b6336ef89 100644 --- a/sass/components/_post.scss +++ b/sass/components/_post.scss @@ -42,7 +42,7 @@ ul { white-space: normal; } - box-shadow: none; + left: 0; position: relative; top: 0; @@ -1786,12 +1786,12 @@ background: linear-gradient(transparent, var(--pinned-highlight-bg-mixed-rgb)); position: relative; } - + .post-collapse__show-more, .post-attachment-collapse__show-more { background: var(--pinned-highlight-bg-mixed-rgb); pointer-events: auto; - } - + } + } } @@ -2052,7 +2052,7 @@ background: rgba(var(--mention-highlight-bg-mixed-rgb), 1); } -.app__body .post-list__table .post.current--user.post--highlight:hover .post-collapse__gradient, +.app__body .post-list__table .post.current--user.post--highlight:hover .post-collapse__gradient, .app__body .post-list__table .post.current--user.post--highlight.post--hovered .post-collapse__gradient { background:linear-gradient(rgba(var(--mention-highlight-bg-mixed-rgb), 0.5), rgba(var(--mention-highlight-bg-mixed-rgb), 1)); } From 19847e026c4272ebd6fc991aee432bb6f1d0b345 Mon Sep 17 00:00:00 2001 From: catalintomai <56169943+catalintomai@users.noreply.github.com> Date: Mon, 29 Mar 2021 23:49:03 -0700 Subject: [PATCH 05/28] =?UTF-8?q?MM-32882:=20Add=20an=20unread=20badge/mar?= =?UTF-8?q?ker=20to=20the=20Main=20Menu=20icon=20and=20the=20=E2=80=98Plug?= =?UTF-8?q?in=20Marketplace=E2=80=99=20Menu=20Item=20(#7563)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- actions/websocket_actions.jsx | 8 ++++ .../legacy_sidebar/header/dropdown/index.js | 5 +++ .../dropdown/sidebar_header_dropdown.jsx | 8 +++- components/legacy_sidebar/header/index.js | 5 ++- .../header/sidebar_header_dropdown_button.jsx | 45 ++++++++++++++++++- components/main_menu/index.jsx | 2 + components/main_menu/main_menu.jsx | 2 + components/plugin_marketplace/index.ts | 6 +++ .../marketplace_modal.test.tsx | 2 + .../plugin_marketplace/marketplace_modal.tsx | 7 +++ .../toggle_modal_button_redux.jsx | 11 +++++ .../menu_item_toggle_modal_redux.tsx | 4 +- .../src/action_types/general.ts | 2 + .../src/actions/general.test.js | 29 ++++++++++++ .../mattermost-redux/src/actions/general.ts | 30 +++++++++++++ .../mattermost-redux/src/client/client4.ts | 19 ++++++++ .../src/constants/websocket.ts | 1 + .../src/reducers/entities/general.test.js | 44 ++++++++++++++++++ .../src/reducers/entities/general.ts | 11 +++++ .../src/selectors/entities/general.test.js | 29 ++++++++++++ .../src/selectors/entities/general.ts | 4 ++ .../src/store/initial_state.ts | 1 + .../mattermost-redux/src/types/general.ts | 6 +++ .../main_menu_action.test.jsx.snap | 1 + sass/components/_buttons.scss | 9 ++++ .../_sidebar-header-dropdown-button.scss | 20 +++++++++ utils/constants.jsx | 1 + 27 files changed, 308 insertions(+), 4 deletions(-) create mode 100644 packages/mattermost-redux/src/reducers/entities/general.test.js diff --git a/actions/websocket_actions.jsx b/actions/websocket_actions.jsx index a0b22f791172..be3bcef36e20 100644 --- a/actions/websocket_actions.jsx +++ b/actions/websocket_actions.jsx @@ -481,6 +481,9 @@ export function handleEvent(msg) { case SocketEvents.CLOUD_PAYMENT_STATUS_UPDATED: dispatch(handleCloudPaymentStatusUpdated(msg)); break; + case SocketEvents.FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED: + handleFirstAdminVisitMarketplaceStatusReceivedEvent(msg); + break; default: } @@ -1359,3 +1362,8 @@ export function handleUserActivationStatusChange() { function handleCloudPaymentStatusUpdated() { return (doDispatch) => doDispatch(getCloudSubscription()); } + +function handleFirstAdminVisitMarketplaceStatusReceivedEvent(msg) { + var receivedData = JSON.parse(msg.data.firstAdminVisitMarketplaceStatus); + store.dispatch({type: GeneralTypes.FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, data: receivedData}); +} diff --git a/components/legacy_sidebar/header/dropdown/index.js b/components/legacy_sidebar/header/dropdown/index.js index ff59998d10d3..bb7a7d620672 100644 --- a/components/legacy_sidebar/header/dropdown/index.js +++ b/components/legacy_sidebar/header/dropdown/index.js @@ -4,6 +4,8 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; +import {getFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/actions/general'; +import {getFirstAdminVisitMarketplaceStatus as firstAdminVisitMarketplaceStatus} from 'mattermost-redux/selectors/entities/general'; import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; import {getInt} from 'mattermost-redux/selectors/entities/preferences'; @@ -19,12 +21,14 @@ function mapStateToProps(state) { const currentTeam = getCurrentTeam(state); const currentUser = getCurrentUser(state); const showTutorialTip = getInt(state, Preferences.TUTORIAL_STEP, currentUser.id) === TutorialSteps.MENU_POPOVER && !Utils.isMobile(); + return { currentUser, teamDescription: currentTeam.description, teamDisplayName: currentTeam.display_name, teamId: currentTeam.id, showTutorialTip, + firstAdminVisitMarketplaceStatus: firstAdminVisitMarketplaceStatus(state), }; } @@ -32,6 +36,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ openModal, + getFirstAdminVisitMarketplaceStatus, }, dispatch), }; } diff --git a/components/legacy_sidebar/header/dropdown/sidebar_header_dropdown.jsx b/components/legacy_sidebar/header/dropdown/sidebar_header_dropdown.jsx index 7afd849697bf..ab478fd42f25 100644 --- a/components/legacy_sidebar/header/dropdown/sidebar_header_dropdown.jsx +++ b/components/legacy_sidebar/header/dropdown/sidebar_header_dropdown.jsx @@ -16,6 +16,8 @@ import MenuWrapper from 'components/widgets/menu/menu_wrapper'; import MainMenu from 'components/main_menu'; +import {isAdmin} from 'utils/utils.jsx'; + export default class SidebarHeaderDropdown extends React.PureComponent { static propTypes = { teamDescription: PropTypes.string.isRequired, @@ -23,8 +25,10 @@ export default class SidebarHeaderDropdown extends React.PureComponent { teamId: PropTypes.string.isRequired, currentUser: PropTypes.object, showTutorialTip: PropTypes.bool.isRequired, + firstAdminVisitMarketplaceStatus: PropTypes.bool.isRequired, actions: PropTypes.shape({ - openModal: PropTypes.func.isRequred, + openModal: PropTypes.func.isRequired, + getFirstAdminVisitMarketplaceStatus: PropTypes.func.isRequired, }).isRequired, }; @@ -74,6 +78,8 @@ export default class SidebarHeaderDropdown extends React.PureComponent { teamDisplayName={this.props.teamDisplayName} teamId={this.props.teamId} openModal={this.props.actions.openModal} + getFirstAdminVisitMarketplaceStatus={this.props.actions.getFirstAdminVisitMarketplaceStatus} + showUnread={isAdmin(this.props.currentUser.roles) && !this.props.firstAdminVisitMarketplaceStatus} /> diff --git a/components/legacy_sidebar/header/index.js b/components/legacy_sidebar/header/index.js index f7a5dc7ab619..98856942771e 100644 --- a/components/legacy_sidebar/header/index.js +++ b/components/legacy_sidebar/header/index.js @@ -3,7 +3,7 @@ import {connect} from 'react-redux'; -import {getConfig} from 'mattermost-redux/selectors/entities/general'; +import {getConfig, getFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/selectors/entities/general'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; import {getInt} from 'mattermost-redux/selectors/entities/preferences'; @@ -20,9 +20,12 @@ function mapStateToProps(state) { const showTutorialTip = getInt(state, Preferences.TUTORIAL_STEP, currentUser.id) === TutorialSteps.MENU_POPOVER && !Utils.isMobile(); + const firstAdminVisitMarketplaceStatus = getFirstAdminVisitMarketplaceStatus(state); + return { enableTutorial, showTutorialTip, + firstAdminVisitMarketplaceStatus, }; } diff --git a/components/legacy_sidebar/header/sidebar_header_dropdown_button.jsx b/components/legacy_sidebar/header/sidebar_header_dropdown_button.jsx index 047f87c7c295..f4ca0eb406f0 100644 --- a/components/legacy_sidebar/header/sidebar_header_dropdown_button.jsx +++ b/components/legacy_sidebar/header/sidebar_header_dropdown_button.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {Tooltip} from 'react-bootstrap'; -import {localizeMessage} from 'utils/utils.jsx'; +import {isAdmin, localizeMessage} from 'utils/utils.jsx'; import OverlayTrigger from 'components/overlay_trigger'; import MenuIcon from 'components/widgets/icons/menu_icon'; import Constants, {ModalIdentifiers} from 'utils/constants'; @@ -22,8 +22,18 @@ export default class SidebarHeaderDropdownButton extends React.PureComponent { currentUser: PropTypes.object.isRequired, teamDisplayName: PropTypes.string.isRequired, openModal: PropTypes.func, + getFirstAdminVisitMarketplaceStatus: PropTypes.func, + showUnread: PropTypes.bool, }; + constructor(props) { + super(props); + + this.state = { + showUnread: false, + }; + } + handleCustomStatusEmojiClick = (event) => { event.stopPropagation(); const customStatusInputModalData = { @@ -33,8 +43,31 @@ export default class SidebarHeaderDropdownButton extends React.PureComponent { this.props.openModal(customStatusInputModalData); } + getFirstAdminVisitMarketplaceStatus = async () => { + const {data} = await this.props.getFirstAdminVisitMarketplaceStatus(); + this.setState({showUnread: !data}); + } + + componentDidMount() { + const isSystemAdmin = isAdmin(this.props.currentUser.roles); + if (isSystemAdmin) { + this.getFirstAdminVisitMarketplaceStatus(); + } + } + + componentDidUpdate() { + const isSystemAdmin = isAdmin(this.props.currentUser.roles); + const {showUnread} = this.props; + if (isSystemAdmin && showUnread !== this.state.showUnread) { + // eslint-disable-next-line react/no-did-update-set-state + this.setState({showUnread: this.props.showUnread}); + } + } + render() { let tutorialTip = null; + let badge = null; + if (this.props.showTutorialTip) { tutorialTip = ( @@ -62,6 +95,15 @@ export default class SidebarHeaderDropdownButton extends React.PureComponent { ); } + if (this.state.showUnread) { + badge = ( + + + + + ); + } + return (
+ {badge}
diff --git a/components/main_menu/index.jsx b/components/main_menu/index.jsx index 7c66f3f90d0d..4399a9b24ae9 100644 --- a/components/main_menu/index.jsx +++ b/components/main_menu/index.jsx @@ -7,6 +7,7 @@ import {bindActionCreators} from 'redux'; import { getConfig, getLicense, + getFirstAdminVisitMarketplaceStatus, getSubscriptionStats as selectSubscriptionStats, } from 'mattermost-redux/selectors/entities/general'; import { @@ -99,6 +100,7 @@ function mapStateToProps(state) { showNextSteps: showNextSteps(state), isCloud: getLicense(state).Cloud === 'true', subscriptionStats: selectSubscriptionStats(state), // subscriptionStats are loaded in actions/views/root + firstAdminVisitMarketplaceStatus: getFirstAdminVisitMarketplaceStatus(state), }; } diff --git a/components/main_menu/main_menu.jsx b/components/main_menu/main_menu.jsx index 57b633c5e923..fd37ffc98685 100644 --- a/components/main_menu/main_menu.jsx +++ b/components/main_menu/main_menu.jsx @@ -64,6 +64,7 @@ class MainMenu extends React.PureComponent { showNextStepsTips: PropTypes.bool, isCloud: PropTypes.bool, subscriptionStats: PropTypes.object, + firstAdminVisitMarketplaceStatus: PropTypes.bool, actions: PropTypes.shape({ openModal: PropTypes.func.isRequred, showMentions: PropTypes.func, @@ -327,6 +328,7 @@ class MainMenu extends React.PureComponent { show={!this.props.mobile && this.props.enablePluginMarketplace} dialogType={MarketplaceModal} text={formatMessage({id: 'navbar_dropdown.marketplace', defaultMessage: 'Plugin Marketplace'})} + showUnread={!this.props.firstAdminVisitMarketplaceStatus} /> ; filterPlugins(filter: string): Promise<{error?: Error}>; + setFirstAdminVisitMarketplaceStatus(): Promise; } function mapDispatchToProps(dispatch: Dispatch) { @@ -39,6 +44,7 @@ function mapDispatchToProps(dispatch: Dispatch) { closeModal: () => closeModal(ModalIdentifiers.PLUGIN_MARKETPLACE), fetchPlugins, filterPlugins, + setFirstAdminVisitMarketplaceStatus, }, dispatch), }; } diff --git a/components/plugin_marketplace/marketplace_modal.test.tsx b/components/plugin_marketplace/marketplace_modal.test.tsx index 0d56d84a8541..3d30068cb65c 100644 --- a/components/plugin_marketplace/marketplace_modal.test.tsx +++ b/components/plugin_marketplace/marketplace_modal.test.tsx @@ -117,10 +117,12 @@ describe('components/marketplace/', () => { installedPlugins: [], pluginStatuses: {}, siteURL: 'http://example.com', + firstAdminVisitMarketplaceStatus: false, actions: { closeModal: jest.fn(), fetchPlugins: jest.fn(), filterPlugins: jest.fn(), + setFirstAdminVisitMarketplaceStatus: jest.fn(), }, }; diff --git a/components/plugin_marketplace/marketplace_modal.tsx b/components/plugin_marketplace/marketplace_modal.tsx index 18373eeeb976..bb17491f9079 100644 --- a/components/plugin_marketplace/marketplace_modal.tsx +++ b/components/plugin_marketplace/marketplace_modal.tsx @@ -96,10 +96,12 @@ export type MarketplaceModalProps = { installedPlugins: MarketplacePlugin[]; siteURL: string; pluginStatuses?: Dictionary; + firstAdminVisitMarketplaceStatus: boolean; actions: { closeModal: () => void; fetchPlugins: (localOnly?: boolean) => Promise<{error?: Error}>; filterPlugins(filter: string): Promise<{error?: Error}>; + setFirstAdminVisitMarketplaceStatus: () => void; }; }; @@ -131,6 +133,11 @@ export class MarketplaceModal extends React.PureComponent + ); + } + // removing these three props since they are not valid props on buttons delete props.modalId; delete props.dialogType; delete props.dialogProps; delete props.accessibilityLabel; delete props.actions; + delete props.showUnread; // allow callers to provide an onClick which will be called before the modal is shown let clickHandler = () => this.show(); @@ -81,6 +91,7 @@ class ModalToggleButtonRedux extends React.PureComponent { onClick={clickHandler} > {children} + {badge} ); } diff --git a/components/widgets/menu/menu_items/menu_item_toggle_modal_redux.tsx b/components/widgets/menu/menu_items/menu_item_toggle_modal_redux.tsx index 8dbff2b3a75d..7abf0cb56f62 100644 --- a/components/widgets/menu/menu_items/menu_item_toggle_modal_redux.tsx +++ b/components/widgets/menu/menu_items/menu_item_toggle_modal_redux.tsx @@ -21,9 +21,10 @@ type Props = { className?: string; children?: React.ReactNode; sibling?: React.ReactNode; + showUnread?: boolean; } -export const MenuItemToggleModalReduxImpl: React.FC = ({modalId, dialogType, dialogProps, text, accessibilityLabel, extraText, children, className, sibling}: Props) => ( +export const MenuItemToggleModalReduxImpl: React.FC = ({modalId, dialogType, dialogProps, text, accessibilityLabel, extraText, children, className, sibling, showUnread}: Props) => ( <> = ({modalId, dialogTy 'MenuItem__with-help': extraText, [`${className}`]: className, })} + showUnread={showUnread} > {text && {text}} {extraText && {extraText}} diff --git a/packages/mattermost-redux/src/action_types/general.ts b/packages/mattermost-redux/src/action_types/general.ts index e9a5fde0fe8b..d5e74b5338e1 100644 --- a/packages/mattermost-redux/src/action_types/general.ts +++ b/packages/mattermost-redux/src/action_types/general.ts @@ -42,4 +42,6 @@ export default keyMirror({ WARN_METRICS_STATUS_RECEIVED: null, WARN_METRIC_STATUS_RECEIVED: null, WARN_METRIC_STATUS_REMOVED: null, + + FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED: null, }); diff --git a/packages/mattermost-redux/src/actions/general.test.js b/packages/mattermost-redux/src/actions/general.test.js index 4ef90c4e1ffb..39825ae27f44 100644 --- a/packages/mattermost-redux/src/actions/general.test.js +++ b/packages/mattermost-redux/src/actions/general.test.js @@ -13,6 +13,8 @@ import configureStore from 'mattermost-redux/test/test_store'; import {FormattedError} from './helpers.ts'; +const OK_RESPONSE = {status: 'OK'}; + describe('Actions.General', () => { let store; beforeAll(() => { @@ -187,4 +189,31 @@ describe('Actions.General', () => { assert.equal(nonexistingURL, 'http://nonexisting.url'); }); }); + + it('getFirstAdminVisitMarketplaceStatus', async () => { + const responseData = { + name: 'FirstAdminVisitMarketplace', + value: 'false', + }; + + nock(Client4.getPluginsRoute()). + get('/marketplace/first_admin_visit'). + query(true). + reply(200, responseData); + + await Actions.getFirstAdminVisitMarketplaceStatus()(store.dispatch, store.getState); + const {firstAdminVisitMarketplaceStatus} = store.getState().entities.general; + assert.strictEqual(firstAdminVisitMarketplaceStatus, false); + }); + + it('setFirstAdminVisitMarketplaceStatus', async () => { + nock(Client4.getPluginsRoute()). + post('/marketplace/first_admin_visit'). + reply(200, OK_RESPONSE); + + await Actions.setFirstAdminVisitMarketplaceStatus()(store.dispatch, store.getState); + + const {firstAdminVisitMarketplaceStatus} = store.getState().entities.general; + assert.strictEqual(firstAdminVisitMarketplaceStatus, true); + }); }); diff --git a/packages/mattermost-redux/src/actions/general.ts b/packages/mattermost-redux/src/actions/general.ts index aa5be69f66a9..f17006af89e7 100644 --- a/packages/mattermost-redux/src/actions/general.ts +++ b/packages/mattermost-redux/src/actions/general.ts @@ -200,6 +200,35 @@ export function getWarnMetricsStatus(): ActionFunc { }; } +export function setFirstAdminVisitMarketplaceStatus(): ActionFunc { + return async (dispatch: DispatchFunc) => { + try { + await Client4.setFirstAdminVisitMarketplaceStatus(); + } catch (e) { + dispatch(logError(e)); + return {error: e.message}; + } + dispatch({type: GeneralTypes.FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, data: true}); + return {data: true}; + }; +} + +export function getFirstAdminVisitMarketplaceStatus(): ActionFunc { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + let data; + try { + data = await Client4.getFirstAdminVisitMarketplaceStatus(); + } catch (error) { + forceLogoutIfNecessary(error, dispatch, getState); + return {error}; + } + + data = JSON.parse(data.value); + dispatch({type: GeneralTypes.FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, data}); + return {data}; + }; +} + export default { getPing, getClientConfig, @@ -214,4 +243,5 @@ export default { setUrl, getRedirectLocation, getWarnMetricsStatus, + getFirstAdminVisitMarketplaceStatus, }; diff --git a/packages/mattermost-redux/src/client/client4.ts b/packages/mattermost-redux/src/client/client4.ts index b35916947829..0ee2ea33850b 100644 --- a/packages/mattermost-redux/src/client/client4.ts +++ b/packages/mattermost-redux/src/client/client4.ts @@ -1,5 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. + +import {SystemSetting} from 'mattermost-redux/types/general'; + import {General} from '../constants'; import {ClusterInfo, AnalyticsRow} from 'mattermost-redux/types/admin'; @@ -34,7 +37,9 @@ import { } from 'mattermost-redux/types/config'; import {CustomEmoji} from 'mattermost-redux/types/emojis'; import {ServerError} from 'mattermost-redux/types/errors'; + import {FileInfo, FileUploadResponse, FileSearchResults} from 'mattermost-redux/types/files'; + import { Group, GroupPatch, @@ -2309,6 +2314,20 @@ export default class Client4 { ); } + setFirstAdminVisitMarketplaceStatus = async () => { + return this.doFetch( + `${this.getPluginsRoute()}/marketplace/first_admin_visit`, + {method: 'post', body: JSON.stringify({first_admin_visit_marketplace_status: true})}, + ); + } + + getFirstAdminVisitMarketplaceStatus = async () => { + return this.doFetch( + `${this.getPluginsRoute()}/marketplace/first_admin_visit`, + {method: 'get'}, + ); + }; + getTranslations = (url: string) => { return this.doFetch>( url, diff --git a/packages/mattermost-redux/src/constants/websocket.ts b/packages/mattermost-redux/src/constants/websocket.ts index 41300d01f64b..4d2afc7eedaf 100644 --- a/packages/mattermost-redux/src/constants/websocket.ts +++ b/packages/mattermost-redux/src/constants/websocket.ts @@ -51,5 +51,6 @@ const WebsocketEvents = { THREAD_UPDATED: 'thread_updated', THREAD_FOLLOW_CHANGED: 'thread_follow_changed', THREAD_READ_CHANGED: 'thread_read_changed', + FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED: 'first_admin_visit_marketplace_status_received', }; export default WebsocketEvents; diff --git a/packages/mattermost-redux/src/reducers/entities/general.test.js b/packages/mattermost-redux/src/reducers/entities/general.test.js new file mode 100644 index 000000000000..618ec482ae08 --- /dev/null +++ b/packages/mattermost-redux/src/reducers/entities/general.test.js @@ -0,0 +1,44 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import assert from 'assert'; + +import reducer from 'mattermost-redux/reducers/entities/general'; +import {GeneralTypes} from 'mattermost-redux/action_types'; + +describe('reducers.entities.general', () => { + describe('firstAdminVisitMarketplaceStatus', () => { + it('initial state', () => { + const state = {}; + const action = {}; + const expectedState = {}; + + const actualState = reducer({firstAdminVisitMarketplaceStatus: state}, action); + assert.deepStrictEqual(actualState.firstAdminVisitMarketplaceStatus, expectedState); + }); + + it('FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, empty initial state', () => { + const state = {}; + const action = { + type: GeneralTypes.FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, + data: true, + }; + const expectedState = true; + + const actualState = reducer({firstAdminVisitMarketplaceStatus: state}, action); + assert.deepStrictEqual(actualState.firstAdminVisitMarketplaceStatus, expectedState); + }); + + it('FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, previously populated state', () => { + const state = true; + const action = { + type: GeneralTypes.FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED, + data: true, + }; + const expectedState = true; + + const actualState = reducer({firstAdminVisitMarketplaceStatus: state}, action); + assert.deepStrictEqual(actualState.firstAdminVisitMarketplaceStatus, expectedState); + }); + }); +}); diff --git a/packages/mattermost-redux/src/reducers/entities/general.ts b/packages/mattermost-redux/src/reducers/entities/general.ts index 14d272b14c39..2d9e2517b38c 100644 --- a/packages/mattermost-redux/src/reducers/entities/general.ts +++ b/packages/mattermost-redux/src/reducers/entities/general.ts @@ -125,6 +125,16 @@ function warnMetricsStatus(state: any = {}, action: GenericAction) { } } +function firstAdminVisitMarketplaceStatus(state = false, action: GenericAction) { + switch (action.type) { + case GeneralTypes.FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED: + return action.data; + + default: + return state; + } +} + export default combineReducers({ appState, credentials, @@ -135,4 +145,5 @@ export default combineReducers({ serverVersion, timezones, warnMetricsStatus, + firstAdminVisitMarketplaceStatus, }); diff --git a/packages/mattermost-redux/src/selectors/entities/general.test.js b/packages/mattermost-redux/src/selectors/entities/general.test.js index 89d655fb2965..4874a86f6df1 100644 --- a/packages/mattermost-redux/src/selectors/entities/general.test.js +++ b/packages/mattermost-redux/src/selectors/entities/general.test.js @@ -397,5 +397,34 @@ describe('Selectors.General', () => { expect(Selectors.getFeatureFlagValue(state, 'CoolFeature')).toEqual('true'); }); }); + + describe('firstAdminVisitMarketplaceStatus', () => { + test('should return empty when status does not exist', () => { + const state = { + entities: { + general: { + firstAdminVisitMarketplaceStatus: { + }, + }, + }, + }; + + expect(Selectors.getFirstAdminVisitMarketplaceStatus(state)).toEqual({}); + }); + + test('should return the value of the status', () => { + const state = { + entities: { + general: { + firstAdminVisitMarketplaceStatus: true, + }, + }, + }; + + expect(Selectors.getFirstAdminVisitMarketplaceStatus(state)).toEqual(true); + state.entities.general.firstAdminVisitMarketplaceStatus = false; + expect(Selectors.getFirstAdminVisitMarketplaceStatus(state)).toEqual(false); + }); + }); }); diff --git a/packages/mattermost-redux/src/selectors/entities/general.ts b/packages/mattermost-redux/src/selectors/entities/general.ts index e4702c42e6a4..0872431e5b7e 100644 --- a/packages/mattermost-redux/src/selectors/entities/general.ts +++ b/packages/mattermost-redux/src/selectors/entities/general.ts @@ -104,3 +104,7 @@ export const getManagedResourcePaths: (state: GlobalState) => string[] = createS export const getServerVersion = (state: GlobalState): string => { return state.entities.general.serverVersion; }; + +export function getFirstAdminVisitMarketplaceStatus(state: GlobalState): boolean { + return state.entities.general.firstAdminVisitMarketplaceStatus; +} diff --git a/packages/mattermost-redux/src/store/initial_state.ts b/packages/mattermost-redux/src/store/initial_state.ts index 5c045c6f5323..21fef0151e83 100644 --- a/packages/mattermost-redux/src/store/initial_state.ts +++ b/packages/mattermost-redux/src/store/initial_state.ts @@ -15,6 +15,7 @@ const state: GlobalState = { serverVersion: '', timezones: [], warnMetricsStatus: {}, + firstAdminVisitMarketplaceStatus: false, }, users: { currentUserId: '', diff --git a/packages/mattermost-redux/src/types/general.ts b/packages/mattermost-redux/src/types/general.ts index 7f24264baa2c..4e63adbfa596 100644 --- a/packages/mattermost-redux/src/types/general.ts +++ b/packages/mattermost-redux/src/types/general.ts @@ -11,8 +11,14 @@ export type GeneralState = { config: Partial; dataRetentionPolicy: any; deviceToken: string; + firstAdminVisitMarketplaceStatus: boolean; license: ClientLicense; serverVersion: string; timezones: string[]; warnMetricsStatus: Dictionary; }; + +export type SystemSetting = { + name: string; + value: string; +}; diff --git a/plugins/test/__snapshots__/main_menu_action.test.jsx.snap b/plugins/test/__snapshots__/main_menu_action.test.jsx.snap index f53519593b4b..4303d02b1e42 100644 --- a/plugins/test/__snapshots__/main_menu_action.test.jsx.snap +++ b/plugins/test/__snapshots__/main_menu_action.test.jsx.snap @@ -281,6 +281,7 @@ exports[`plugins/MainMenuActions should match snapshot and click plugin item for id="marketplaceModal" modalId="plugin_marketplace" show={true} + showUnread={true} text="Plugin Marketplace" /> diff --git a/sass/components/_buttons.scss b/sass/components/_buttons.scss index 5fd601de09da..0b0136700bc4 100644 --- a/sass/components/_buttons.scss +++ b/sass/components/_buttons.scss @@ -1,6 +1,15 @@ @charset 'UTF-8'; button { + .unread-badge { + display: inline-block; + border-radius: 50%; + width: 8px; + height: 8px; + margin: 0 0 0 40px; + background: #F74343; + } + &.style--none { background: transparent; border: none; diff --git a/sass/components/_sidebar-header-dropdown-button.scss b/sass/components/_sidebar-header-dropdown-button.scss index f5f4ed15c2aa..511d0d501a49 100644 --- a/sass/components/_sidebar-header-dropdown-button.scss +++ b/sass/components/_sidebar-header-dropdown-button.scss @@ -24,6 +24,26 @@ } } + .unread-badge { + position: absolute; + border-radius: 50%; + width: 8px; + height: 8px; + right: 2px; + bottom: 2px; + background: #F74343; + } + + .unread-badge-addon { + position: absolute; + top: 8px; + right: 4px; + height: 12px; + width: 12px; + border-radius: 32px; + background-color: var(--sidebar-header-bg); + } + .menu-icon { @include opacity(.8); fill: $white; diff --git a/utils/constants.jsx b/utils/constants.jsx index 7a3b920b83ef..18b28596458b 100644 --- a/utils/constants.jsx +++ b/utils/constants.jsx @@ -393,6 +393,7 @@ export const SocketEvents = { SIDEBAR_CATEGORY_ORDER_UPDATED: 'sidebar_category_order_updated', USER_ACTIVATION_STATUS_CHANGED: 'user_activation_status_change', CLOUD_PAYMENT_STATUS_UPDATED: 'cloud_payment_status_updated', + FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED: 'first_admin_visit_marketplace_status_received', }; export const TutorialSteps = { From 58165c9b095ec9a379635dfcb8cd3f05a9ae9aa4 Mon Sep 17 00:00:00 2001 From: Nev Angelova Date: Tue, 30 Mar 2021 13:51:53 +0700 Subject: [PATCH 06/28] [MM-34163][MM-34165] - UI fixes to direct channel profile pics (#7777) * [MM-34163] - UI fixes to direct channel profile pics * Update snapshot * increase status icon bg by 1px Co-authored-by: Nevyana Angelova --- .../__snapshots__/sidebar_direct_channel.test.tsx.snap | 1 + .../sidebar_direct_channel/sidebar_direct_channel.tsx | 2 +- sass/layout/_sidebar-left.scss | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/components/sidebar/sidebar_channel/sidebar_direct_channel/__snapshots__/sidebar_direct_channel.test.tsx.snap b/components/sidebar/sidebar_channel/sidebar_direct_channel/__snapshots__/sidebar_direct_channel.test.tsx.snap index 8f87dc9d232c..75078769322b 100644 --- a/components/sidebar/sidebar_channel/sidebar_direct_channel/__snapshots__/sidebar_direct_channel.test.tsx.snap +++ b/components/sidebar/sidebar_channel/sidebar_direct_channel/__snapshots__/sidebar_direct_channel.test.tsx.snap @@ -73,6 +73,7 @@ exports[`components/sidebar/sidebar_channel/sidebar_direct_channel should match newStatusIcon={true} size="xs" src="/api/v4/users/user_id/image" + status="" statusClass="DirectChannel__status-icon " wrapperClass="DirectChannel__profile-picture" /> diff --git a/components/sidebar/sidebar_channel/sidebar_direct_channel/sidebar_direct_channel.tsx b/components/sidebar/sidebar_channel/sidebar_direct_channel/sidebar_direct_channel.tsx index 53c0477209b1..b50c207c36c2 100644 --- a/components/sidebar/sidebar_channel/sidebar_direct_channel/sidebar_direct_channel.tsx +++ b/components/sidebar/sidebar_channel/sidebar_direct_channel/sidebar_direct_channel.tsx @@ -99,7 +99,7 @@ class SidebarDirectChannel extends React.PureComponent { i { font-size: 18px; - margin: 0 10px 0 -2px; + margin: 0 6px 0 -2px; display: flex; } @@ -1107,7 +1107,7 @@ .DirectChannel__profile-picture { height: 20px; - margin-right: 13px; + margin-right: 9px; .DirectChannel__status-icon { position: absolute; @@ -1115,8 +1115,8 @@ left: 10px; border-radius: 100%; background: var(--sidebar-bg); - height: 12px; - width: 12px; + height: 13px; + width: 13px; font-size: 12px; align-items: center; justify-content: center; @@ -1145,7 +1145,7 @@ .status.status--group { background: rgba(var(--sidebar-text-rgb), 0.16); - margin: 0px 13px 0 1px; + margin: 0px 9px 0 1px; width: 18px; height: 18px; flex-shrink: 0; From 071eef90c01027872c3fa08a352981e6f977ebeb Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Tue, 30 Mar 2021 09:17:19 -0400 Subject: [PATCH 07/28] [MM-32769] Channel Sidebar Tests for category sorting and DM/GM filtering (#7735) * WIP * [MM-32769] Channel Sidebar Tests for category sorting and DM/GM filtering * Moved to seperate files * Remove beforeEach * And admin login * Update e2e/cypress/integration/channel_sidebar/category_sorting_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/channel_sidebar/dm_gm_filtering_sorting_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/channel_sidebar/dm_gm_filtering_sorting_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/channel_sidebar/dm_gm_filtering_sorting_spec.js Co-authored-by: Saturnino Abril Co-authored-by: Saturnino Abril Co-authored-by: Mattermod --- .../channel_sidebar/category_sorting_spec.js | 300 ++++++++++++++++++ .../dm_gm_filtering_sorting_spec.js | 146 +++++++++ 2 files changed, 446 insertions(+) create mode 100644 e2e/cypress/integration/channel_sidebar/category_sorting_spec.js create mode 100644 e2e/cypress/integration/channel_sidebar/dm_gm_filtering_sorting_spec.js diff --git a/e2e/cypress/integration/channel_sidebar/category_sorting_spec.js b/e2e/cypress/integration/channel_sidebar/category_sorting_spec.js new file mode 100644 index 000000000000..163e1fe3864d --- /dev/null +++ b/e2e/cypress/integration/channel_sidebar/category_sorting_spec.js @@ -0,0 +1,300 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// *************************************************************** +// - [#] indicates a test step (e.g. # Go to a page) +// - [*] indicates an assertion (e.g. * Check the title) +// - Use element ID when selecting an element. Create one if none. +// *************************************************************** + +// Group: @channel_sidebar + +import * as TIMEOUTS from '../../fixtures/timeouts'; +import {getRandomId} from '../../utils'; + +let testTeam; +let testUser; +let testChannel; + +describe('Category sorting', () => { + beforeEach(() => { + // # Login as test user and visit town-square + cy.apiAdminLogin(); + cy.apiInitSetup({loginAfter: true}).then(({team, user, channel}) => { + testTeam = team; + testUser = user; + testChannel = channel; + cy.visit(`/${team.name}/channels/town-square`); + }); + }); + + it('MM-T3834 Category sorting', () => { + const channelNames = []; + const categoryName = createCategoryFromSidebarMenu(); + + // # Create 5 channels and add them to a custom category + for (let i = 0; i < 5; i++) { + channelNames.push(createChannelAndAddToCategory(categoryName)); + cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false}); + } + + // # Sort alphabetically + cy.get(`.SidebarChannelGroupHeader:contains(${categoryName})`).within(() => { + // # Open dropdown next to channel name + cy.get('.SidebarMenu').invoke('show').get('.SidebarMenu_menuButton').should('be.visible').click({force: true}); + + // # Open sub menu + cy.get('#sortChannels').parent('.SubMenuItem').trigger('mouseover'); + + // # Click on sort alphabetically + cy.get('#sortAlphabetical').parent('.SubMenuItem').click(); + }); + + // * Verify channels are sorted alphabetically + verifyAlphabeticalSortingOrder(categoryName, channelNames.length); + + // # Add another channel + channelNames.push(createChannelAndAddToCategory(categoryName)); + cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false}); + + // * Verify channels are still sorted alphabetically + verifyAlphabeticalSortingOrder(categoryName, channelNames.length); + + // # Sort by recency + cy.get(`.SidebarChannelGroupHeader:contains(${categoryName})`).within(() => { + // # Open dropdown next to channel name + cy.get('.SidebarMenu').invoke('show').get('.SidebarMenu_menuButton').should('be.visible').click({force: true}); + + // # Open sub menu + cy.get('#sortChannels').parent('.SubMenuItem').trigger('mouseover'); + + // # Click on sort by recency + cy.get('#sortByMostRecent').parent('.SubMenuItem').click(); + }); + + // # Sort channel names in reverse order that they were created (ie. most recent to least) + let sortedByRecencyChannelNames = channelNames.concat().reverse(); + for (let i = 0; i < channelNames.length; i++) { + // * Verify that the channels are in reverse order that they were created + cy.get(`.SidebarChannelGroup:contains(${categoryName}) .NavGroupContent li:nth-child(${i + 1}) a[id^="sidebarItem_${sortedByRecencyChannelNames[i]}"]`).should('be.visible'); + } + + // # Add another channel + channelNames.push(createChannelAndAddToCategory(categoryName)); + cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false}); + + // # Sort channel names in reverse order that they were created (ie. most recent to least) + sortedByRecencyChannelNames = channelNames.concat().reverse(); + for (let i = 0; i < channelNames.length; i++) { + // * Verify that the channels are still in reverse order that they were created + cy.get(`.SidebarChannelGroup:contains(${categoryName}) .NavGroupContent li:nth-child(${i + 1}) a[id^="sidebarItem_${sortedByRecencyChannelNames[i]}"]`).should('be.visible'); + } + + // # Remove the oldest from the category and put it into Favourites + cy.get(`.SidebarChannelGroup:contains(${categoryName}) .NavGroupContent a[id^="sidebarItem_${channelNames[0]}"]`).should('be.visible').within(() => { + // # Open dropdown next to channel name + cy.get('.SidebarMenu').invoke('show').get('.SidebarMenu_menuButton').should('be.visible').click({force: true}); + + // # Open sub menu + cy.get('li[id^="moveTo-"]').trigger('mouseover'); + + // # Click on move to new category + cy.get('div.SubMenuItemContainer:nth-child(1) li').click(); + }); + + // * Verify the channel is now in Favourites + cy.get(`.SidebarChannelGroup:contains(FAVORITES) .NavGroupContent a[id^="sidebarItem_${channelNames[0]}"]`).should('be.visible'); + channelNames.shift(); + + // # Sort manually + cy.get(`.SidebarChannelGroupHeader:contains(${categoryName})`).within(() => { + // # Open dropdown next to channel name + cy.get('.SidebarMenu').invoke('show').get('.SidebarMenu_menuButton').should('be.visible').click({force: true}); + + // # Open sub menu + cy.get('#sortChannels').parent('.SubMenuItem').trigger('mouseover'); + + // # Click on sort manually + cy.get('#sortManual').parent('.SubMenuItem').click(); + }); + + // # Add another channel + channelNames.push(createChannelAndAddToCategory(categoryName)); + cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false}); + + // * Verify that the channel has been placed at the bottom of the category + cy.get(`.SidebarChannelGroup:contains(${categoryName}) .NavGroupContent li:nth-child(1) a[id^="sidebarItem_${channelNames[channelNames.length - 1]}"]`).should('be.visible'); + }); + + it('MM-T3916 Create Category character limit', () => { + // # Click on the sidebar menu dropdown + cy.findByLabelText('Add Channel Dropdown').click(); + + // # Click on create category link + cy.findByText('Create New Category').should('be.visible').click(); + + // # Add a name 26 characters in length e.g `abcdefghijklmnopqrstuvwxyz` + cy.get('#editCategoryModal').should('be.visible').wait(TIMEOUTS.HALF_SEC).within(() => { + cy.findByText('Create New Category').should('be.visible'); + + // # Enter category name + cy.findByPlaceholderText('Name your category').should('be.visible').type('abcdefghijklmnopqrstuvwxyz'); + }); + + // * Verify error state and negative character count at the end of the textbox based on the number of characters the user has exceeded + cy.get('#editCategoryModal .MaxLengthInput.has-error').should('be.visible'); + cy.get('#editCategoryModal .MaxLengthInput__validation').should('be.visible').should('contain', '-4'); + + // * Verify Create button is disabled. + cy.get('#editCategoryModal .GenericModal__button.confirm').should('be.visible').should('be.disabled'); + + // # Use backspace to remove 4 characters + cy.get('#editCategoryModal .MaxLengthInput').should('be.visible').type('{backspace}{backspace}{backspace}{backspace}'); + + // * Verify error state and negative character count at the end of the textbox are no longer displaying + cy.get('#editCategoryModal .MaxLengthInput.has-error').should('not.be.visible'); + cy.get('#editCategoryModal .MaxLengthInput__validation').should('not.be.visible'); + + // * Verify Create button is enabled + cy.get('#editCategoryModal .GenericModal__button.confirm').should('be.visible').should('not.be.disabled'); + + // Click Create + cy.get('#editCategoryModal .GenericModal__button.confirm').should('be.visible').click(); + + // Verify new category is created + cy.findByLabelText('abcdefghijklmnopqrstuv').should('be.visible'); + }); + + it('MM-T3864 Sticky category headers', () => { + const categoryName = createCategoryFromSidebarMenu(); + + // # Move test channel to Favourites + cy.get(`#sidebarItem_${testChannel.name}`).parent().then((element) => { + // # Get id of the channel + const id = element[0].getAttribute('data-rbd-draggable-id'); + cy.get(`#sidebarItem_${testChannel.name}`).parent('li').within(() => { + // # Open dropdown next to channel name + cy.get('.SidebarMenu').invoke('show').get('.SidebarMenu_menuButton').should('be.visible').click({force: true}); + + // # Favourite the channel + cy.get(`#favorite-${id} button`).should('be.visible').click({force: true}); + }); + }); + + // # Create 15 channels and add them to a custom category + for (let i = 0; i < 15; i++) { + createChannelAndAddToCategory(categoryName); + cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false}); + } + + // # Create 10 channels and add them to Favourites + for (let i = 0; i < 10; i++) { + createChannelAndAddToFavourites(); + cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false}); + } + + // # Scroll to the center of the channel list + cy.get('#SidebarContainer .scrollbar--view').scrollTo('center', {ensureScrollable: false}); + + // * Verify that both the 'More Unreads' label and the category header are visible + cy.get('#unreadIndicatorTop').should('be.visible'); + cy.get('#SidebarContainer .SidebarChannelGroupHeader:contains(FAVORITES)').should('be.visible'); + + // # Scroll to the bottom of the list + cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false}); + + // * Verify that the 'More Unreads' label is still visible but the category is not + cy.get('#unreadIndicatorTop').should('be.visible'); + cy.get('#SidebarContainer .SidebarChannelGroupHeader:contains(FAVORITES)').should('not.be.visible'); + }); +}); + +function createChannelAndAddToCategory(categoryName) { + const channelName = `channel-${getRandomId()}`; + const userId = testUser.id; + cy.apiCreateChannel(testTeam.id, channelName, 'New Test Channel').then(({channel}) => { + // # Add the user to the channel + cy.apiAddUserToChannel(channel.id, userId).then(() => { + // # Move to a new category + cy.get(`#sidebarItem_${channel.name}`).parent().then((element) => { + // # Get id of the channel + const id = element[0].getAttribute('data-rbd-draggable-id'); + cy.get(`.SidebarChannelGroup:contains(${categoryName})`).should('be.visible').then((categoryElement) => { + const categoryId = categoryElement[0].getAttribute('data-rbd-draggable-id'); + + cy.get(`#sidebarItem_${channel.name}`).parent('li').within(() => { + // # Open dropdown next to channel name + cy.get('.SidebarMenu').invoke('show').get('.SidebarMenu_menuButton').should('be.visible').click({force: true}); + + // # Open sub menu + cy.get(`#moveTo-${id}`).parent('.SubMenuItem').trigger('mouseover'); + + // # Click on move to new category + cy.get(`#moveToCategory-${id}-${categoryId}`).parent('.SubMenuItem').click(); + }); + }); + }); + }); + }); + return channelName; +} + +function createChannelAndAddToFavourites() { + const userId = testUser.id; + cy.apiCreateChannel(testTeam.id, `channel-${getRandomId()}`, 'New Test Channel').then(({channel}) => { + // # Add the user to the channel + cy.apiAddUserToChannel(channel.id, userId).then(() => { + // # Move to a new category + cy.get(`#sidebarItem_${channel.name}`).parent().then((element) => { + // # Get id of the channel + const id = element[0].getAttribute('data-rbd-draggable-id'); + cy.get(`#sidebarItem_${channel.name}`).parent('li').within(() => { + // # Open dropdown next to channel name + cy.get('.SidebarMenu').invoke('show').get('.SidebarMenu_menuButton').should('be.visible').click({force: true}); + + // # Favourite the channel + cy.get(`#favorite-${id} button`).should('be.visible').click({force: true}); + }); + }); + }); + }); +} + +function verifyAlphabeticalSortingOrder(categoryName, length) { + // # Go through each channel to get its name + const sortedAlphabeticalChannelNames = []; + for (let i = 0; i < length; i++) { + // Grab the elements in the order that they appear in the sidebar + cy.get(`.SidebarChannelGroup:contains(${categoryName}) .NavGroupContent li:nth-child(${i + 1}) .SidebarChannelLinkLabel`).should('be.visible').invoke('text').then((text) => { + sortedAlphabeticalChannelNames.push(text); + + // # Sort the names manually + const comparedSortedChannelNames = sortedAlphabeticalChannelNames.concat().sort((a, b) => a.localeCompare(b, 'en', {numeric: true})); + + // * Verify that the sorted order matches the order they were already in + assert.deepEqual(sortedAlphabeticalChannelNames, comparedSortedChannelNames); + }); + } +} + +function createCategoryFromSidebarMenu() { + // # Start with a new category + const categoryName = `category-${getRandomId()}`; + + // # Click on the sidebar menu dropdown + cy.findByLabelText('Add Channel Dropdown').click(); + + // # Click on create category link + cy.findByText('Create New Category').should('be.visible').click(); + + // # Verify that Create Category modal has shown up. + // # Wait for a while until the modal has fully loaded, especially during first-time access. + cy.get('#editCategoryModal').should('be.visible').wait(TIMEOUTS.HALF_SEC).within(() => { + cy.findByText('Create New Category').should('be.visible'); + + // # Enter category name and hit enter + cy.findByPlaceholderText('Name your category').should('be.visible').type(categoryName).type('{enter}'); + }); + + return categoryName; +} diff --git a/e2e/cypress/integration/channel_sidebar/dm_gm_filtering_sorting_spec.js b/e2e/cypress/integration/channel_sidebar/dm_gm_filtering_sorting_spec.js new file mode 100644 index 000000000000..0145a4b0be53 --- /dev/null +++ b/e2e/cypress/integration/channel_sidebar/dm_gm_filtering_sorting_spec.js @@ -0,0 +1,146 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// *************************************************************** +// - [#] indicates a test step (e.g. # Go to a page) +// - [*] indicates an assertion (e.g. * Check the title) +// - Use element ID when selecting an element. Create one if none. +// *************************************************************** + +// Group: @dm_category + +import {getAdminAccount} from '../../support/env'; + +describe('DM/GM filtering and sorting', () => { + const sysadmin = getAdminAccount(); + let testUser; + before(() => { + // # Login as test user and visit town-square + cy.apiInitSetup({loginAfter: true}).then(({team, user}) => { + testUser = user; + cy.visit(`/${team.name}/channels/town-square`); + + // # upgrade user to sys admin role + cy.externalRequest({user: sysadmin, method: 'put', path: `users/${user.id}/roles`, data: {roles: 'system_user system_admin'}}); + }); + }); + + it('MM-T2003 Number of direct messages to show', () => { + const receivingUser = testUser; + + // * Verify that we can see the sidebar + cy.get('#headerTeamName').should('be.visible'); + + // # Collapse the DM category (so that we can check all unread DMs quickly without the sidebar scrolling being an issue) + cy.get('button.SidebarChannelGroupHeader_groupButton:contains(DIRECT MESSAGES)').should('be.visible').click(); + + // # Create 41 DMs (ie. one over the max displayable read limit) + for (let i = 0; i < 41; i++) { + // # Create a new user to have a DM with + cy.apiCreateUser().then(({user}) => { + cy.apiCreateDirectChannel([receivingUser.id, user.id]).then(({channel}) => { + // # Post a message as the new user + cy.postMessageAs({ + sender: user, + message: `Hey ${receivingUser.username}`, + channelId: channel.id, + }); + + // * Verify that the DM count is now correct + cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) a[id^="sidebarItem"]').should('have.length', Math.min(i + 1, 20)); + + // # Click on the new DM channel to mark it read + cy.get(`#sidebarItem_${channel.name}`).should('be.visible').click(); + }); + }); + } + + // # Expand the DM category (so that we can check all unread DMs quickly without the sidebar scrolling being an issue) + cy.get('button.SidebarChannelGroupHeader_groupButton:contains(DIRECT MESSAGES)').should('be.visible').click(); + + // * Verify that there are 20 DMs shown in the sidebar + cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) a[id^="sidebarItem"]').should('have.length', 20); + + // # Go to Sidebar Settings + navigateToSidebarSettings(); + + // * Verify that the default setting for DMs shown is 20 + cy.get('#limitVisibleGMsDMsDesc').should('be.visible').should('contain', '20'); + + // # Click Edit + cy.get('#limitVisibleGMsDMsEdit').should('be.visible').click(); + + // # Change the value to All Direct Messages + cy.get('#limitVisibleGMsDMs').should('be.visible').click(); + cy.get('.react-select__option:contains(All Direct Messages)').should('be.visible').click(); + + // # Click Save + cy.get('#saveSetting').should('be.visible').click(); + + // # Close Account Settings + cy.get('#accountSettingsHeader > .close').click(); + + // * Verify that there are 41 DMs shown in the sidebar + cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) a[id^="sidebarItem"]').should('have.length', 41); + + // # Go to Sidebar Settings + navigateToSidebarSettings(); + + // # Click Edit + cy.get('#limitVisibleGMsDMsEdit').should('be.visible').click(); + + // # Change the value to 10 + cy.get('#limitVisibleGMsDMs').should('be.visible').click(); + cy.get('.react-select__option:contains(10)').should('be.visible').click(); + + // # Click Save + cy.get('#saveSetting').should('be.visible').click(); + + // # Close Account Settings + cy.get('#accountSettingsHeader > .close').click(); + + // * Verify that there are 10 DMs shown in the sidebar + cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) a[id^="sidebarItem"]').should('have.length', 10); + }); + + it('MM-T3832 DMs/GMs should not be removed from the sidebar when only viewed (no message)', () => { + cy.apiCreateUser().then(({user}) => { + cy.apiCreateDirectChannel([testUser.id, user.id]).then(({channel}) => { + // # Post a message as the new user + cy.postMessageAs({ + sender: user, + message: `Hey ${testUser.username}`, + channelId: channel.id, + }); + + // # Click on the new DM channel to mark it read + cy.get(`#sidebarItem_${channel.name}`).should('be.visible').click(); + + // # Click on Town Square + cy.get('.SidebarLink:contains(Town Square)').should('be.visible').click(); + + // * Verify we're on Town Square + cy.url().should('contain', 'town-square'); + + // # Refresh the page + cy.visit('/'); + + // * Verify that the DM we just read remains in the sidebar + cy.get(`#sidebarItem_${channel.name}`).should('be.visible'); + }); + }); + }); +}); + +function navigateToSidebarSettings() { + cy.get('#channel_view').should('be.visible'); + cy.get('#sidebarHeaderDropdownButton').should('be.visible').click(); + cy.get('#accountSettings').should('be.visible').click(); + cy.get('#accountSettingsModal').should('be.visible'); + + cy.get('#sidebarButton').should('be.visible'); + cy.get('#sidebarButton').click(); + + cy.get('#sidebarLi.active').should('be.visible'); + cy.get('#sidebarTitle > .tab-header').should('have.text', 'Sidebar Settings'); +} From 7cae3da7ae16d83edd5b9a35b9563fd127f3d7a6 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Tue, 30 Mar 2021 19:33:55 +0530 Subject: [PATCH 08/28] websocket_client: rename variables (#7772) Rename some variables and add comments for clarity Co-authored-by: Mattermod --- client/websocket_client.tsx | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/client/websocket_client.tsx b/client/websocket_client.tsx index 4f2c65032283..42758ece00db 100644 --- a/client/websocket_client.tsx +++ b/client/websocket_client.tsx @@ -8,8 +8,15 @@ const MAX_WEBSOCKET_RETRY_TIME = 300000; // 5 mins export default class WebSocketClient { private conn: WebSocket | null; private connectionUrl: string | null; - private sequence: number; - private eventSequence: number; + + // responseSequence is the number to track a response sent + // via the websocket. A response will always have the same sequence number + // as the request. + private responseSequence: number; + + // serverSequence is the incrementing sequence number from the + // server-sent event stream. + private serverSequence: number; private connectFailCount: number; private eventCallback: ((msg: any) => void) | null; private responseCallbacks: {[x: number]: ((msg: any) => void)}; @@ -22,8 +29,8 @@ export default class WebSocketClient { constructor() { this.conn = null; this.connectionUrl = null; - this.sequence = 1; - this.eventSequence = 0; + this.responseSequence = 1; + this.serverSequence = 0; this.connectFailCount = 0; this.eventCallback = null; this.responseCallbacks = {}; @@ -52,7 +59,7 @@ export default class WebSocketClient { this.connectionUrl = connectionUrl; this.conn.onopen = () => { - this.eventSequence = 0; + this.serverSequence = 0; if (token) { this.sendMessage('authentication_challenge', {token}); @@ -72,7 +79,7 @@ export default class WebSocketClient { this.conn.onclose = () => { this.conn = null; - this.sequence = 1; + this.responseSequence = 1; if (this.connectFailCount === 0) { console.log('websocket closed'); //eslint-disable-line no-console @@ -125,11 +132,11 @@ export default class WebSocketClient { Reflect.deleteProperty(this.responseCallbacks, msg.seq_reply); } } else if (this.eventCallback) { - if (msg.seq !== this.eventSequence && this.missedEventCallback) { - console.log('missed websocket event, act_seq=' + msg.seq + ' exp_seq=' + this.eventSequence); //eslint-disable-line no-console + if (msg.seq !== this.serverSequence && this.missedEventCallback) { + console.log('missed websocket event, act_seq=' + msg.seq + ' exp_seq=' + this.serverSequence); //eslint-disable-line no-console this.missedEventCallback(); } - this.eventSequence = msg.seq + 1; + this.serverSequence = msg.seq + 1; this.eventCallback(msg); } }; @@ -161,7 +168,7 @@ export default class WebSocketClient { close() { this.connectFailCount = 0; - this.sequence = 1; + this.responseSequence = 1; if (this.conn && this.conn.readyState === WebSocket.OPEN) { this.conn.onclose = () => {}; //eslint-disable-line no-empty-function this.conn.close(); @@ -173,7 +180,7 @@ export default class WebSocketClient { sendMessage(action: string, data: any, responseCallback?: () => void) { const msg = { action, - seq: this.sequence++, + seq: this.responseSequence++, data, }; From 146be81d9c87f4964b84af6b76564345ca44b22b Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Tue, 30 Mar 2021 23:11:33 +0800 Subject: [PATCH 09/28] Cypress/E2E: Promote tests to prod (#7780) * promote tests to prod * revert archived_leave_channel_spec --- e2e/cypress/integration/custom_status/custom_status_1_spec.js | 1 + e2e/cypress/integration/custom_status/custom_status_2_spec.js | 1 + e2e/cypress/integration/custom_status/custom_status_3_spec.js | 3 ++- e2e/cypress/integration/custom_status/custom_status_4_spec.js | 1 + e2e/cypress/integration/custom_status/custom_status_6_spec.js | 1 + .../enterprise/integrations/incoming_webhook_spec.js | 1 + 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/e2e/cypress/integration/custom_status/custom_status_1_spec.js b/e2e/cypress/integration/custom_status/custom_status_1_spec.js index 4e4d01c057c5..9e7b6606b5ad 100644 --- a/e2e/cypress/integration/custom_status/custom_status_1_spec.js +++ b/e2e/cypress/integration/custom_status/custom_status_1_spec.js @@ -7,6 +7,7 @@ // - Use element ID when selecting an element. Create one if none. // *************************************************************** +// Stage: @prod // Group: @custom_status describe('Custom Status - CTAs for New Users', () => { diff --git a/e2e/cypress/integration/custom_status/custom_status_2_spec.js b/e2e/cypress/integration/custom_status/custom_status_2_spec.js index fdb95428e957..de060dcaaa60 100644 --- a/e2e/cypress/integration/custom_status/custom_status_2_spec.js +++ b/e2e/cypress/integration/custom_status/custom_status_2_spec.js @@ -7,6 +7,7 @@ // - Use element ID when selecting an element. Create one if none. // *************************************************************** +// Stage: @prod // Group: @custom_status describe('Custom Status - Setting a Custom Status', () => { diff --git a/e2e/cypress/integration/custom_status/custom_status_3_spec.js b/e2e/cypress/integration/custom_status/custom_status_3_spec.js index 154cec33733d..3d8de77c72f4 100644 --- a/e2e/cypress/integration/custom_status/custom_status_3_spec.js +++ b/e2e/cypress/integration/custom_status/custom_status_3_spec.js @@ -7,6 +7,7 @@ // - Use element ID when selecting an element. Create one if none. // *************************************************************** +// Stage: @prod // Group: @custom_status import {openCustomStatusModal} from './helper'; @@ -48,7 +49,7 @@ describe('Custom Status - Setting Your Own Custom Status', () => { cy.get('#emojiPicker').should('exist'); }); - it('MM_T3846_3 should select the emoji from the emoji picker', () => { + it('MM-T3846_3 should select the emoji from the emoji picker', () => { // * Check that the emoji picker is open cy.get('#emojiPicker').should('exist'); diff --git a/e2e/cypress/integration/custom_status/custom_status_4_spec.js b/e2e/cypress/integration/custom_status/custom_status_4_spec.js index 7807b585b816..3dc4ca7c2ee6 100644 --- a/e2e/cypress/integration/custom_status/custom_status_4_spec.js +++ b/e2e/cypress/integration/custom_status/custom_status_4_spec.js @@ -7,6 +7,7 @@ // - Use element ID when selecting an element. Create one if none. // *************************************************************** +// Stage: @prod // Group: @custom_status import {openCustomStatusModal} from './helper'; diff --git a/e2e/cypress/integration/custom_status/custom_status_6_spec.js b/e2e/cypress/integration/custom_status/custom_status_6_spec.js index dc4546fa951c..aef4adca71ce 100644 --- a/e2e/cypress/integration/custom_status/custom_status_6_spec.js +++ b/e2e/cypress/integration/custom_status/custom_status_6_spec.js @@ -7,6 +7,7 @@ // - Use element ID when selecting an element. Create one if none. // *************************************************************** +// Stage: @prod // Group: @custom_status describe('Custom Status - Slash Commands', () => { diff --git a/e2e/cypress/integration/enterprise/integrations/incoming_webhook_spec.js b/e2e/cypress/integration/enterprise/integrations/incoming_webhook_spec.js index b482af9e68d7..0c47f2b8109b 100644 --- a/e2e/cypress/integration/enterprise/integrations/incoming_webhook_spec.js +++ b/e2e/cypress/integration/enterprise/integrations/incoming_webhook_spec.js @@ -7,6 +7,7 @@ // - Use element ID when selecting an element. Create one if none. // *************************************************************** +// Stage: @prod // Group: @enterprise @incoming_webhook @not_cloud import * as TIMEOUTS from '../../../fixtures/timeouts'; From 337d028e49f8a6d1e0deeba437e834262740230a Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Tue, 30 Mar 2021 17:01:11 -0400 Subject: [PATCH 10/28] add selectors for app bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed commit of the following: commit ab0b9e88a14f2a3e49b3a87eabe31421ed72522e Author: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Tue Mar 30 16:36:33 2021 -0400 make appBindings an optional prop commit 264e8b05671b92a34bf0620fecea60bd14d0c47d Author: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Mon Mar 29 03:21:25 2021 -0400 add selectors for apps bindings commit d7f792eab390256180e131f5d902cd52535fdd61 Merge: 3fd6e9325 e53a8358f Author: Daniel Espino García Date: Wed Mar 24 12:36:44 2021 +0100 Merge branch 'master' into feature/cloud-apps commit 3fd6e9325c79beae42d0ebd041fc93b1e1009a0c Author: Daniel Espino García Date: Wed Mar 24 12:22:54 2021 +0100 Remove Enable/Disable and Remove from System Console for cloud apps (#7651) * Remove Enable/Disable and Remove from System Console for cloud apps * Set buttons as null * Allow change the status when the feature flag is on * Fix test * Update snapshots Co-authored-by: Mattermod commit 7b4be8ebf51f251451f4900d1e5a363884306c93 Author: Daniel Espino García Date: Wed Mar 24 12:11:01 2021 +0100 Case independency fixes (#7722) commit af6ba8797cd5c5387f2ed77fddcad82ddde81932 Author: Daniel Espino García Date: Wed Mar 24 12:08:19 2021 +0100 Use Feature flag (#7643) * Use Feature flag * Fix test and minor improvement * Simplify return commit de7bca6c0782b47e2af101b2555894e0d80bf129 Author: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Wed Mar 17 10:29:36 2021 -0400 Synchronize app command parser with mobile app (#7706) * Use type on call path, and minor refactoring * Add missing i18n * move files into app_command_parser folder * Fix navigation and small fix on error handling * include mobile app changes to command parser * i18n * types and lint * use intl * change i18n ids * tests * test fixes * add more tests for button_binding and select_binding * lint * fix merege issue * incorporate changes from #7703 * add translation * fix and i18n * remove unused translation Co-authored-by: Daniel Espino García commit b54d6c52cda1c48adeb83e24f43901e3fdca7dc6 Merge: 05a669c46 ee77df3f0 Author: Daniel Espino García Date: Wed Mar 17 12:15:35 2021 +0100 Merge branch 'master' into feature/cloud-apps commit 05a669c46cfb85adc8e02f3ab2ea97a5a9ffe7e3 Author: Daniel Espino García Date: Wed Mar 17 04:45:19 2021 +0100 Use type on call path, and minor refactoring (#7679) * Use type on call path, and minor refactoring * Add missing i18n * Fix navigation and small fix on error handling * Update redux reference * Fix types * Fix test * Fix test * Address PR Feedback commit 9b1cbbff02a092ef9a7d3a26fe0e016c257c4545 Author: Daniel Espino García Date: Wed Mar 10 15:32:16 2021 +0100 Remove autocomplete of ~ and @ for user and channel suggestions (#7667) commit 00685fc935148b00ab6049ffb68e63a0a7a3399f Author: Daniel Espino García Date: Wed Mar 10 15:29:52 2021 +0100 Fix command errors (#7664) commit 83a24ddc0d313c492410e644232720cee4f4dc94 Author: Daniel Espino García Date: Wed Mar 10 15:27:04 2021 +0100 Command send options (user, channel, dynamic and static) as options and not only values. (#7650) * Command send options (user, channel, dynamic and static) as options and not only values. * Fetch users from server or generate error * Fix i18 and fix minor typo commit 6c1fea615501be4924d3fb9eaf8a987fdbe2552c Merge: d06cdffb8 507991558 Author: Daniel Espino García Date: Tue Mar 9 11:14:07 2021 +0100 Merge branch 'master' into feature/cloud-apps commit d06cdffb86def63d4fa67f145775d6b6ce3deb3d Author: Daniel Espino García Date: Mon Mar 8 15:08:35 2021 +0100 Fix multiword select options and keep quotes on complete values (#7644) * Fix multiword select options and keep quotes on complete values * Fix test commit e1d67a169ae88d43c9f76f0dc11d94db7e9722df Author: Daniel Espino García Date: Sat Mar 6 11:55:43 2021 +0100 Fix channel header dropdown icon size and count (#7642) commit c5c86ca148002ac11a97035fbba6d50f55630fde Author: Daniel Espino García Date: Thu Mar 4 11:47:21 2021 +0100 All select use AppSelectOption (#7612) * All select use AppSelectOption * Fix test commit 2f0ab8f9e9fd602fad5cf4d6cf258dbda5b95c8c Author: Ben Schumacher Date: Thu Mar 4 11:13:45 2021 +0100 Use App id to install App from Marketplace (#7631) * Use App id to install App from Marketplace * Fix tests commit 94a86bec10017df9f22bc78ad04aa0d2596c3e24 Author: Daniel Espino García Date: Tue Mar 2 09:24:35 2021 +0100 Add refresh websocket event (#7599) * Add refresh websocket event * Make more obvious the App framework events * Fix typo and separate handler into a function commit bde2a3488503b60cd5deb273f29ad035f93c0dfb Author: Daniel Espino García Date: Tue Mar 2 06:25:03 2021 +0100 Handle errors on binding locations (#7556) * Handle errors on binding locations * Fix types * Fix lint * Translate and handle all messages directly in the binding * Fix various errors * Fix snapshot commit 8868f33acc0a1fa089354ce7fa9e99dcfd404eec Merge: 2e3c92e28 db27a519d Author: Daniel Espino García Date: Mon Mar 1 12:02:52 2021 +0100 Merge branch 'master' into feature/cloud-apps commit 2e3c92e2827fc37890796768e157c16042b4e4e4 Author: Daniel Espino García Date: Fri Feb 26 23:00:48 2021 +0100 Fix App parser (#7555) * Fix parser * Fix lint * Fix test commit c9dc94fd80bf6fbb1e18c2e3e1d88b4f5d3eff47 Author: Ben Schumacher Date: Fri Feb 26 19:17:30 2021 +0100 [MM-31508] Rename URL to Path in Call (#7566) commit 6b0675fcadee8ee24df6bdf7c4ff713e27a732c9 Merge: 5790e6316 82790213b Author: Daniel Espino García Date: Wed Feb 24 10:03:00 2021 +0100 Merge branch 'master' into feature/cloud-apps commit 5790e6316b5110b8828a768a4ee37d92d107c91c Author: Daniel Espino García Date: Tue Feb 16 12:48:49 2021 +0100 Rework embedded forms (#7423) * Rework embedded forms * Improve naming * Improve naming, improve binding filling and trimming and use binding as the base element of a EmbeddedBinding * Move appsEnabled to utils/apps.ts * Add a create request function, add logging, and check for undefined bindings. commit 5f2660f6adefcf1668f71f905622f4a150d5f95d Merge: faaebe611 f7ea0ec34 Author: Daniel Espino García Date: Mon Feb 15 14:22:55 2021 +0100 Merge branch 'master' into feature/cloud-apps commit faaebe6115684c7cae5abd748d963980f9626a9e Author: Lev <1187448+levb@users.noreply.github.com> Date: Sat Feb 13 15:53:56 2021 -0800 Refactored apps command parser (#7426) * Fixed command autocomplete for no fields * make test * small fix: restore * wip * wip wip * wip appears to compile * first tests * Consolidated TC type and checks * ParseForm tests work, again * ParseForm tests work, again * unit tests work * nit * PR feedback, some debugging * PR feedback * PR feedback: moved methods into ParsedCommand * merging... * more merging * more more merging commit 50babb5bcef3dbc044e2cb69ef40cee69fefa820 Author: Daniel Espino García Date: Wed Feb 10 19:00:55 2021 +0100 [MM-31088] Set submit buttons to the bottom of the form (#7473) * Set submit buttons to the bottom of the form * Fix typescript * Fix test * Fix lint commit e5619c9a1cc49f246c43ddd116b8b62946907bf5 Author: Daniel Espino García Date: Wed Feb 10 15:31:50 2021 +0100 Isolate Apps to disable them whenever needed (#7376) * Isolate Apps to disable them whenever needed * Use feature flags * Isolate command_provider * Change shouldProcessApps by a more meaningful name appsEnabled * Fix test commit 1f866cdfd6972607decdbe64014ebb7d7566ad3f Merge: e65f8e066 3a24c6a0c Author: Daniel Espino García Date: Wed Feb 10 13:23:13 2021 +0100 Merge branch 'master' into feature/cloud-apps commit e65f8e06613fc5a5d31573071c42d407dc799462 Author: Ben Schumacher Date: Tue Feb 9 09:59:38 2021 +0100 [MM-32476] Apps in the Marketplace (#7474) commit 93e7c3421fef3f7f7a50bbc095512116f7976553 Merge: b2ef64e41 00b9e12d0 Author: Ben Schumacher Date: Thu Feb 4 13:20:10 2021 +0100 Merge remote-tracking branch 'origin/master' into feature/cloud-apps commit b2ef64e4132f500dd4d3c7226b87da8c58bd3a25 Author: Daniel Espino García Date: Mon Feb 1 12:58:03 2021 +0100 Update redux reference commit 63f211746e975e5509d7d749c9c26d2cfcd39963 Merge: 837694a9b 0de03d607 Author: Daniel Espino García Date: Mon Feb 1 12:54:11 2021 +0100 Merge branch 'master' into feature/cloud-apps commit 837694a9b3a61911cd3c3e9cf21b678b3182b6fa Merge: c1cd297e9 b819372f0 Author: Ben Schumacher Date: Fri Jan 29 13:53:47 2021 +0100 Merge remote-tracking branch 'origin/master' into feature/cloud-apps commit c1cd297e94a9dcaea00508ad11c696d512365fdd Author: Daniel Espino García Date: Wed Jan 27 11:58:32 2021 +0100 Add Navigate to AppCallResonses handling (#7377) commit 44d85a493644efff7bd6363d95f888b681ada21d Author: Jason Frerich Date: Wed Jan 27 04:58:12 2021 -0600 use label tag, not paragraph (#7378) commit 0411421a0af3d014c1adbf15c16f431d835e88bc Author: Daniel Espino García Date: Tue Jan 26 17:44:12 2021 +0100 Fix Embedded forms after refactor (#7331) * Fix Embedded forms after refactor * Fix lint commit 9eba5ecc26885cc0e252c4d7ac2fbbff8510a078 Merge: 4f5f04144 b252c8a44 Author: Daniel Espino García Date: Mon Jan 25 12:31:46 2021 +0100 Merge branch 'master' into feature/cloud-apps commit 4f5f04144375d6a2ce591265e8c78d3eab40919e Merge: 4716206ff 97e741e1d Author: Daniel Espino García Date: Mon Jan 25 11:30:51 2021 +0100 Merge branch 'master' into feature/cloud-apps commit 4716206ffa48b174a8a8aa7882158c43c0f2ab3a Author: Daniel Espino García Date: Mon Jan 18 12:16:56 2021 +0100 Point to latest redux commit 0122d36712de26a51440ced499d77fdd956fe7aa Merge: f27d6e1cf 19b189c04 Author: Daniel Espino García Date: Mon Jan 18 12:02:26 2021 +0100 Merge branch 'master' into feature/cloud-apps commit f27d6e1cf898eb71f5424c9520ef81c76a8ae28b Author: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Thu Jan 14 17:09:42 2021 -0500 Dynamic Apps Modals (#7166) * First approach on embedded forms * Fix lint * Render form in the RHS * Send needed information along the form * Address feedback * Use proper redux version * Add embedded buttons * Handle embedded forms as new modals * Improve state handling * Remove unneeded changes * Fix lint * Migrate button_selector to typescript and make buttons disabled * Fix lint * Make errors work on Embedded and Modal forms * Handle button field type from app forms * Consider button fields also select fields and handle submit_buttons field * dynamic select works. modal can refresh on submit * updates for refreshing on select * fix types * mm-redux * types * snapshot * change react-select import * fix react-select type * submit buttons * error handling * import lint * fix embedded form props * lint * updates for call structure * lint * mm-redux * move presentation field from call to binding * update tests * update prop for test * modal maintains its own state until it unmounts/closes * fix test * mm-redux and fix * fixes for using form call * remove dispatch * fix post id * fix command lookups. allow commands to open modals through binding's presentation * mm-redux * remove interactive dialog field references * fix test * test updates * use global store for command_provider, which is mocked in test * call.type == 'form' && response.type == 'form' opens a modal * lint * remove makeLookupCallPayload * lint * fix imports * fix import Co-authored-by: Daniel Espino García commit 6f9f30379191db3f9988113ffdc950a2b2fbff86 Author: Daniel Espino García Date: Thu Jan 14 14:59:49 2021 +0100 Fix missing channel header buttons (#7322) commit be9f4ff9bc770dff1723a8938010537486de7812 Author: Daniel Espino García Date: Wed Jan 13 09:48:41 2021 +0100 Merge master to feature branch (#7250) * [MM-30193] change file attachment info to show fractions below 10 (#6935) * change fileinfo to show fractions below 10M * fix formatting * add linting fixes and clean up code * fix incorrect brackets Co-authored-by: Mattermod * Revert "Mm 29111 search bar and search results as fc (#6796)" (#6999) This reverts commit 9fba9a9789e3e1e6194daa36aabe343859e8511b. * [MM-20196] Use UTC timezone for formattedDate so it doesn't change the date (#7000) * Use UTC timezone for formattedDate so it doesn't change the date * Add toUTCString * Remove it because it isn't needed * Add to last invoice summary * Make invoice.tax a boolean so a rogue 0 isn't rendered (#7001) * Translations update from Weblate (#7006) * Translated using Weblate (Dutch) Currently translated at 99.9% (4294 of 4295 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.5% (4275 of 4295 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.6% (4270 of 4285 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Spanish) Currently translated at 99.2% (4264 of 4295 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Japanese) Currently translated at 98.2% (4219 of 4295 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ Translated using Weblate (Japanese) Currently translated at 98.2% (4219 of 4295 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4295 of 4295 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ Translated using Weblate (Turkish) Currently translated at 96.0% (4126 of 4295 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ Co-authored-by: Tom De Moor Co-authored-by: Elias Nahum Co-authored-by: kaakaa Co-authored-by: Kaya Zeren * [MM-30066] Allow sidebar category button width to grow and shrink as needed (#6955) Co-authored-by: Mattermod * Migrate team_list and team_row to typescript (#6914) Co-authored-by: i.nikolaievskyi * [MM-20482] Migrate 'components/post_view/channel_intro_message' module … (#6729) * MM-28843 Adding feature flags to system console for easy diagnosis. (#6905) * Adding feature flags to system console for easy diagnosis. * Use AdminConfig type. * Redux update. * [MM-29850] Migrate string-refs to functional ones: this.refs.errorMessage (#6881) * Update string ref to functional ref * Make ref load before use Co-authored-by: Mattermod * [MM-30190] Show spinner when loading invoices instead of 'no invoices found' (#7012) * [MM-T560] Add test for notifications (#6884) Co-authored-by: Saturnino Abril * MM-20492 Migrate 'components/post_view/post_message_view' module and … (#6738) * Cypress/E2E: Fix custom categories spec (#7011) * [MM-30216] - Add check for undefined fileInfos (#7019) Co-authored-by: Nevyana Angelova * [MM-20465] migrate single_image_view to typescript (#6774) * migrate single_image_view to typescript * fix unit test * resolove review comment * Update components/single_image_view/single_image_view.tsx * Guard clause fileinfo` Co-authored-by: Mattermod Co-authored-by: Nev Angelova Co-authored-by: Nevyana Angelova * [MM-29903] - Check if current user is on last post when scrolling in rhs (#6920) * Scroll user to bottom when they are on the latest post in rhs * fix tests * Fix type check * Implement using getSnapshotBeforeUpdate * Remove comment box and set refs * Update scroll logic * resize rhs container correctly * Calculate with header height * fix type error * please linter * update snapshot Co-authored-by: Nevyana Angelova Co-authored-by: Nevyana Angelova * MM-T614 Search on Outgoing Webhooks page (#6873) * wip * cleanup, optimize * remove file Co-authored-by: Mattermost Co-authored-by: Mattermod * Cypress/E2E: Move cluster spec to enterprise (#7010) * Cypress/E2E: Move cluster spec to enterprise * Add @enterprise tag * MM-20578 Migrate 'components/admin_console/system_user_detail' module… (#6801) * [MM-20568] Migrate 'components/admin_console/system_users' module to ts (#6829) * Fixing title text in select team icon. (#7023) * MM-30090 Open managed resource links in a new tab (#7024) * MM-30090 Open managed resource links in a new tab * MM-30090 Add ManagedResourcePaths setting * Update test case to reflect new case * Fix types * Revert unnecessary type changes * Switch mattermost-redux back to master * MM-27903, MM-27899: Folded Reply Threads: ThreadItem and ThreadFooter (#6585) * wip: checkpoint * wip: fix tooltip * wip: checkpoint * move connected profile_picture back out of widgets * timestamp improvements * wip: threading components * wip: thread footer * timestamp story improvments * wip: threading components * wip: threading components * wip: threading comps * wip: thread menu * update testing * wip: testing updates * unit tests, storybook cleanup * tweak timestamp knob arrangement * i18n * stray apostrophe; snapshots-i18n * profile_picture index * misc cleanup * put prop-types rule back in * simple_tooltip remove FC * sync relative time spec * update snapshots * Update index.stories.js * Update story_box.tsx * remove trailing space * update url * remove frag * sb container width * review fixes * review fixes/improvements * review/other changes * simplify * actually add nowrap * fix new messages tooltip style * cleanup * improvements, clarity * clearer conditionals * remove unused css/simplify * use text-clamp mixin, fix knob typo * remove unused css props * remove empty file * fix bad role attr * sync css; test appended button * review changes * badge as button if it has a click handler * update snapshot Co-authored-by: Mattermod * migrate string-refs in user_list.jsx (#6704) * migrate string-refs in user_list.jsx * add unit test for user_list.jsx * remove the eslint-enable/disable comments + rename this.container Co-authored-by: Mattermod * [MM-T556] Add e2e test for MM-T556 (#7016) * Add e2e test for MM-T556 Add e2e test for Browser tab and team sidebar notification - no unreads/mentions Github Issues: Fixes https://github.com/mattermost/mattermost-server/issues/15705 Jira Ticket: MM-T556 * Fix finicky test * Move Remove mention notification to before() * Fix broken test * Linting Co-authored-by: Mattermod * MM-24505-Update-help-text-leave-team-modal (#6572) * message update for leave team modal and updated in i18en.json * implemented dynamic channl values and changed tests * update failing snapshot * added new strings for public and priivate cases * updated desc * update plurals in string * removed console log statement * modal description based on the counts * i18n order changed * i18n corrected * resolved suggestions * updated failing snapshot * corrected messages in modals * updated snapshot * rewordings to modal texts * guest modal message * guest modal condition added along with modal message * updated failing snapshot * added currentuser prop to the test file * Fixed type error in test file * Fixes condition * conditions for zero public & zero private for guest * modified conditions * Fixed modal messages * snapshot updated * Fixes * MM-29929 Fix for unread messages toast on marking channel as unread (#7028) * The initOffset we use for displaying toast should be only used mount as that is not a relative scroll position and is only set on mount * Changing this to show unread toast on all occasions of marking channel as unread other than channel at bottom * [MM-26386] - Respect user locale when searching for channels (#6904) Co-authored-by: Nevyana Angelova Co-authored-by: Mattermod * [MM-25186] Bot username validation i8n support (#6629) * Migrate String Refs to Functional ones - MM 16039 (#6865) * MM-29849/MM-29942/MM-29982 Fix code theme not being applied (#7008) * Convert 'components/post_view/post_info' module and associated tests to TypeScript (#6619) * Convert 'components/post_view/post_info' module and associated tests to TypeScript. Based on conversations with @devin and @abdu - marking certain props as non-mandatory. * Fixing some review comments. * Fixing some review comments. * Fixing some review comments. * Fixing some review comments. * Fixing some issues from rebasing from latest master branch * Handling null checks for 'getCurrentChannel' calls. * Fixing issue with white page. * Fixing issue with check types. Co-authored-by: Mattermod * [MM-30183] - No more text decoration on hover and focus (#7020) * Fix no text decoration on hover and focus * revert extra space Co-authored-by: Mattermod * [MM-20470] Migrate emoji page and any team permission gate to typescript #15470 (#6645) * Migrate emoji page and any team permission gate to typescript * Convert delete_emoji_modal to typescript Co-authored-by: i.nikolaievskyi Co-authored-by: Mattermod * [MM-30226][MM-39255] Allow a post to be sent when you do CMD + ENTER on Mac with 'Allow all messages' to be sent via CMD + ENTER (#7031) * make a post be able to send when you do command enter when editing post * fix linting * Edit task to take into control mac command key * fix linting * [MM-30340] Update post height when comment box height change occurs (#7039) * [MM-30340] Update post height when comment box height change occurs * Update snaps * Add scroll to bottom to the resize function * Remove redundant check Co-authored-by: Nevyana Angelova * Translations update from Weblate (#7046) * Translated using Weblate (Japanese) Currently translated at 98.1% (4219 of 4298 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4302 of 4302 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ Translated using Weblate (Turkish) Currently translated at 100.0% (4298 of 4298 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 96.3% (4147 of 4302 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Dutch) Currently translated at 99.6% (4310 of 4323 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.9% (4301 of 4302 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Korean) Currently translated at 79.4% (3418 of 4302 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ * Translated using Weblate (Japanese) Currently translated at 97.5% (4220 of 4328 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Translated using Weblate (Korean) Currently translated at 79.7% (3453 of 4328 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 97.3% (4214 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 97.3% (4215 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Co-authored-by: kaakaa Co-authored-by: Kaya Zeren Co-authored-by: rodrigocorsi Co-authored-by: Tom De Moor Co-authored-by: Ji-Hyeon Gim * [MM-28181] e2e: add system_console/unsaved_changes_spec (#7009) * e2e: add system_console/unsaved_changes_spec * reflect revivew comments * 15474 convert team selector modal to ts (#6824) * Convert 'components/team_selector_modal' module and associated tests to TypeScript. * Fixing review comments. * Fixing review comments. * Fixing review comments. * Fixing bad merge Co-authored-by: Mattermod * MM-19509 Prioritize autocomplete results based on interactions and threads (#7002) * MM-19509 Prioritize autocomplete results based on interactions and threads * Add sort method of auto complete results to be based on last_viewed_at as well * Add new prop priorityResults to auto complete so we can add profiles like thread interactions on top * Fix sorting for remote results * Add E2E test for desktop notifications for empty strings (#6799) * Add E2E test for desktop notifications for empty strings * Update comments based on feedback * Typo Co-authored-by: Mattermod * MM-20433 - convert view_pinned_posts to typescript (#7034) * MM-20433 - convert view_pinned_posts to typescript * fix linter errors Co-authored-by: Pablo Velez Co-authored-by: Mattermod * Cypress/E2E: fix test (#7052) * fix e2e test * make trial user configurable via env * fix order * [MM-29855] Migrate string-refs to functional ones: this.refs.content (#6867) * Update string refs to functional component based refs * Update string refs to functional component based refs * [MM-27231]: e2e for T1880 - deleted token cannot be used to post (#6901) * update api method to return more data, and add method to revoke token * test for T1880 - deletd token can't be used to post * fix comment * move methods to better place Co-authored-by: Mattermod * Fix menu rendering behind input box in RHS. (#7061) * [MM-27708] Disable Plugin on Removal (#6518) * [MM-30104][MM-30105] Fix up banners in payment_info and billing_subscription pages (#7063) * Fix up banners in payment_info and billing_subscription pages * Reset package-lock * Remove comment * [MM-27556] UI For new system roles (#6529) * Add translations * Basic skeleton without subsections visible * Allow roles to be saved and fix up styling * Dont allow sysadmins to be edited and dont allow self to be removed * Cleanup * Add translations * Save state properly * Update redux * Minor styling changes * Update redux * Bug fixes * Rename datagrid search to onSearch * Cleanup * UX fixes * Fix snaps * Forgot to check this in * Fix console error * Fix snaps * Expand by default if mixed access * update package json files * address PR comments * update redux hash * update redux hash * make disabled * add set navgiation blocked * Fix hash * make necessary changes * make read only admin editable * update function name * fix type check * fix lint issue * udpate redux hash * update search * update snap test * fix set navigation blocked after save * make conditional rendering for system manager * fix linting * go back after saving successfully * updat redux hash * Add Beta Tag Co-authored-by: Mattermod Co-authored-by: Hossein Ahmadian-Yazdi * [MM-20428] Migrate terms of service to Typescript (#6965) * First pass * Added types everywhere * Fixed ESLint warnings and errors * Fix incorrect import * Updated snapshot * PR review feedback Co-authored-by: Christophe Carpentier Co-authored-by: Mattermod * [MM-29621] Only load the stripe library when cloud components are actually rendered (#7067) * Only load the stripe library when cloud components are actually rendered * Use proper return type * Remove async * MM-20412: Migrate 'components/channel_header_mobile/show_search_button' module to TypeScript (#6969) Co-authored-by: Mattermod * MM 20404 Migrate 'components/announcement_bar/version_bar' module and associated tests to TypeScript (#6976) * MM-20404 * MM-20404 Migrate 'components/announcement_bar/version_bar' module and associated tests to TypeScript * fix lint * MM-20404 Migrate 'components/announcement_bar/version_bar' module and associated tests to TypeScript * fix linter Co-authored-by: Mattermod * MM-27229 - Cypress/E2E: Automate backlogs - BOT Accounts - Part 06 (10 test cases) (#6772) Co-authored-by: Prapti Co-authored-by: Caleb Roseland Co-authored-by: Mattermod * migrate refs icon_url. #15801 (#6754) Automatic Merge * [GH-16035] Migrating refs plugin_management.tsx (#6866) Automatic Merge * [MM-29515] Hide imports tab when in cloud installation (#7069) * Hide imports tab when in cloud installation * fix types and linter * Revert package-lock to master * Make isCloud optional * MM-T446 DM More... searching from page 2 of user list (#7054) * wip * wip * revision * selector fix * fix Co-authored-by: Mattermod * DOPS-243 (#7075) DOPS-243 (#7075) * [GH-13483] Migrate 'components/channel_invite_modal' module and associated tests to TypeScript (#6968) * [WIP] migration to typescript * [WIP] further typescript changes * finalized move of component to typescript * [WIP] move index file to typescript * updated test to typescript * fix for types in index file * removed failing class import * [No Ticket] Update to latest redux hash and fixing test cases with missing types (#7084) Automatic Merge * [MM-27154] Cypress tests: Incoming Webhooks (#6977) * Incoming webhooks cypress tests * Remove unused variable * PR feedback * add license * PR feedback and lint * fix import path Co-authored-by: Saturnino Abril Co-authored-by: Mattermod Co-authored-by: Saturnino Abril * fix failing tests due to announcement bar (#7077) * MM-27315 Cypress tests for Integrations > Slash command auto-complete (7 test cases) (#6909) * MM-30359: Cypress/E2E: Account Settings > Email (#7051) * MM-30359: Cypress/E2E: Account Settings > Email This PR automates the following test cases: MM-T2066 MM-T2067 MM-T2068 MM-T2069 MM-T2070 MM-T2071 MM-T2072 MM-T2073 https://mattermost.atlassian.net/browse/MM-30359 ```release-note NONE ``` * fix lint * add the other tests * Address review comments Co-authored-by: Mattermod * [MM-T561] Add e2e test for MM-T561 (#7015) * Add e2e test Add test for: Browser tab and team sidebar - direct messages don't add indicator on team icon in team sidebar (but do in browser tab) Github Issues: Fix https://github.com/mattermost/mattermost-server/issues/15700 Jira Ticket: MM-T561 * Apply suggested review * Add cleanup for MM-T560_2 * Linting * Separate test file * Linting * Add prerequisite * Rename file * Remove old test Co-authored-by: Mattermod * [MM-28255] Add tests for OAuth Apps (#6558) * Add tests for OAuth Apps * Add remaining tests * Update test names, fix typos and add explicit status codes * Add group and license check * Move tests to enterprise folder Co-authored-by: Mattermod * MM-29858: Migrate string refs to functional ones in autosize textarea component (#6885) * MM-29858: Migrate string refs to functional ones in autosize textarea component * MM-29858: Rename reference instance variable with suffix Ref * MM-29858: Use camel casing for textarea reference variable * MM-29858: Allow empty value for textarea Co-authored-by: Mattermod * Migrate errorMessage string ref (#6882) Automatic Merge * MM-T329 Image link preview (#7005) * wip * wip * finished * revision * remove log Co-authored-by: Mattermod * [MM-28150] e2e: add MM-T924, MM-T928, MM-T929, MM-T930 (#7045) * e2e: add MM-T924, MM-T928, MM-T929, MM-T930 * reflect review comments Co-authored-by: Mattermod * MM-30363: Cypress/E2E: Automate backlogs - Edit Bot Username (1 test case) (#7073) * MM-30373 - first iteration * MM-30377 - first iteration * remove spurious file * MM-30377 - further iteration * MM-30363 - first iteration * remove extra file * some cleanup * remoe extra space Co-authored-by: Catalin Tomai Co-authored-by: Mattermod * promote and demote tests to/from prod (#7080) * Cypress/E2E: Fix toast appears unread spec (#7081) * change super to system (#7076) * MM-20418: Migrate 'components/channel_selector_modal' module and associated tests to TypeScript (#6975) * DOPS-243: Fix images (#7089) DOPS-243: Fix images (#7089) * MM-T439 Town Square is not marked as unread for existing users when a new user is added to the team (#7053) Automatic Merge * [MM-20582] Migrate 'components/admin_console/team_channel_settings/ch… (#6840) * MM-20465 Fix for collapse/expand of image preview (#7085) * The toggle function requires id and not post * This was most likely changed to fix a ts error * MM-20465 Revert usage of getCurrentChannel to getCurrentChannelId where possible (#7064) * [MM-29821] Prefer TypeScript files over JavaScript in webpack (#7090) * [MM-30317] add MANAGE_REMOTE_CLUSTERS permission (#7060) * add MANAGE_REMOTE_CLUSTERS permission * Update i18n/en.json Co-authored-by: Doug Lauder Co-authored-by: Doug Lauder * MM-20416: Migrate 'components/channel_header_mobile/collapse_lhs_button' module to TypeScript (#6971) Co-authored-by: Mattermod * Remove Cloud billing flag from System Console (#7096) (cherry picked from commit 6c35b79ac663756b5e31f0b878531aabec68efd3) Co-authored-by: Maria A Nunez * Translations update from Weblate (#7100) * Translated using Weblate (Turkish) Currently translated at 100.0% (4390 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ Translated using Weblate (Turkish) Currently translated at 100.0% (4329 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (German) Currently translated at 90.4% (3915 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/de/ * Translated using Weblate (Korean) Currently translated at 80.1% (3468 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (4328 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Russian) Currently translated at 95.0% (4113 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ru/ * Translated using Weblate (Dutch) Currently translated at 98.5% (4328 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 98.4% (4322 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 96.7% (4246 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Romanian) Currently translated at 93.8% (4119 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 92.7% (4072 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Dutch) Currently translated at 98.5% (4325 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Co-authored-by: Kaya Zeren Co-authored-by: Elisabeth Kulzer Co-authored-by: Ji-Hyeon Gim Co-authored-by: rodrigocorsi Co-authored-by: Alexey Napalkov Co-authored-by: Tom De Moor Co-authored-by: aeomin Co-authored-by: Viorel-Cosmin Miron * Migrate 'components/announcement_bar/default_announcement_bar' module and associated tests to TypeScript (#6963) * rename file * working except for test file * restore unincluded test file to jsx * pass npm make check * update snapshot * respond to review comments * checkout package-lock.json * revert package-lock.json changes * fix linting errors Co-authored-by: Mattermod * Migrate this.ref.dotMenu to functional (#6957) * Migrate this.ref.dotMenu to functional * Fixed formatting * Fix reference * rename dotMenu to dotMenuRef for consistency with other PRs, remove comments * Removed blanked line to pass lint check Co-authored-by: root Co-authored-by: Mattermod * MM-20897 Add category muting (#7033) * MM-20897 Add ability to mute categories * Increase timeout to ensure test passes * Add E2E test for moving channels into muted categories * Update mattermost-redux to branch * Fix unit tests * Fix types * Update snapshot * asdf * Add additional E2E test and API client infrastructure * MM-24293 Fix to have group channels return in quick switcher with space (#7059) * MM-24293 Fix to have group channels return in quick siwchter with space * Split the search term by spaces and match with users in group channels * Update components/suggestion/switch_channel_provider.jsx Co-authored-by: Guillermo Vayá Co-authored-by: Guillermo Vayá Co-authored-by: Mattermod * If card year is 0 return false for isExpired (#7101) * Converted 'dot_menu' to typescript. (#6825) * Migrate string refs of reset password modal (#7038) Automatic Merge * MM-20415 Migrate 'components/channel_header_mobile/unmute_channel_button' module and associated tests to TypeScript (#6972) * MM-30477 Fix for autocomplete not closing (#7093) Automatic Merge * MM-20897 Cleanup a couple missed issues (#7106) * MM-T638 Webhook posts when webhook creator is not a member of the channel (#7091) * wip * wip * wip * wip * added check * small fix * fix Co-authored-by: Mattermod * [MM-20481] Migrate 'components/post_view/post_body_additional_content' module and associated tests to TypeScript (#6668) * [MM-20481] Migrate 'components/post_view/post_body_additional_content' module and associated tests to TypeScript Fixes https://github.com/mattermost/mattermost-server/issues/15460 JIRA: https://mattermost.atlassian.net/browse/MM-20481 * Make actions attribute non nullable * Require YoutubeVideo's postId property since it really is * Fix tests * Add stages for onprem * Add build-vars stage * set isDisabled prop for site > notices (#7103) Automatic Merge * [MM-27931][MM-30158] Multi-selection and dragging of channels (#6979) * [MM-27931] Multiselection of channels * Test fix * Lint fix * Change opacity of selected state to 0.24 * Added hover state for selected channels * Tests for multiSelect * Drag and drop working (without visuals, missing redux commit) * Multi drag and drop styles (fade the selected channels, might change) * Don't allow multisellected channels to be dropped where they shouldn't be * Lint, type, test, translation fixes * Allow dragging of non matching channel groups by rejecting the channels that don't match * Redux update * Another test fix * Fixed selected count style to work off of center channel exclusively * Added really obvious styling * Style change/PR feedback * Style change * Include active channel in all initial selections * PR feedback * Add constant for shifted index Co-authored-by: Mattermod * [MM-20572] Migrate 'components/admin_console/compliance_reports' to TypeScript (#13500) (#7036) Automatic Merge * MM-30087 Remove direct dependency between Client4 and Rudder (#7056) * MM-30087 Remove direct dependency between Client4 and Rudder * Update tests * Switch mattermost-redux to master * Fix missing types * Change how we mock and import rudder * Remove test that checks if events are sent to Rudder * [MM_20423]'external_image'module to ts (#16151) (#7025) - migrate ts index.js file - migrate ts for external_image.tsx - migrate ts for external_images.test.jsx Co-authored-by: Hossein Ahmadian-Yazdi * Fix issue 16144: migrate components/claim module and tests to TypeScript (#7043) Automatic Merge * MM-T2056 e2e to test, Username change reflecting for other users (#7074) Co-authored-by: Mattermod * [MM-26334] Fixes direct messages loading screen misalignment (#6813) Summary: Reduces the height of the loading screen so it is more centered and/or unintrusive(small screens) enough to remove the scroll bar during loading. Ticket Link: Fixes https://github.com/mattermost/mattermost-server/issues/15926 * Translations update from Weblate (#7122) * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Romanian) Currently translated at 93.8% (4121 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 98.9% (4345 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4392 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Spanish) Currently translated at 97.7% (4292 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Dutch) Currently translated at 98.8% (4341 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Co-authored-by: Viorel-Cosmin Miron Co-authored-by: aeomin Co-authored-by: Kaya Zeren Co-authored-by: Elias Nahum Co-authored-by: Tom De Moor * MM-28153: Cypress/E2E: Automate backlogs - System Console > Environment (8 test cases) (#7049) * MM-30377: Cypress/E2E: Automate backlogs - Messaging > Channel and Post Links (6 test cases) (#7072) * MM-30355: Cypress/E2E: Automate backlogs - Account Settings > Username (6 test cases) (#7062) * MM-27208 Add tests for MM-T1684, MM-T1687, and MM-T1688 (#7124) * MM-27208 MM-T1684 Add test number to existing test case * Fix makeClient for requests without a body * MM-27208 MM-T1687 Add test for another user archiving a channel * MM-27208 MM-T1688 Add test for searching in archived channels * Translations update from Weblate (#7139) * Translated using Weblate (Spanish) Currently translated at 99.1% (4357 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Dutch) Currently translated at 98.8% (4344 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Dutch) Currently translated at 98.9% (4349 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.4% (4327 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Co-authored-by: Elias Nahum Co-authored-by: Tom De Moor Co-authored-by: rodrigocorsi * [MM-20498] components/post_view/combined_system_message' to TypeScript (#7109) * Added Initial Ts conversion to last_users and last_user_test * Converted combined system message * Completed all files conversion * Updated Snapshots * Minor fix * Minor Fixes * Minor Fixes * Migrate 'components/sidebar_right_menu' module to TypeScript (#6962) * Converted 'leave_channel' to typescript. (#7115) Co-authored-by: Mattermod * [MM-20581] Migrate index to TS in 'components/admin_console/team_channel_settings/channel/details' module (#6924) * mark browser.focused as false if starting in the background (#7050) Co-authored-by: Mattermod * MM-27989 Remove experimental setting for data prefetch (#7094) Co-authored-by: Harrison Healey * [MM-27207] e2e leave archived channels I - last (#7117) Automatic Merge * MM-30370: Cypress/E2E: Automate backlogs - Keyboard Shortcuts (10 test cases) (#7113) Automatic Merge * MM-30371: Cypress/E2E: Automate backlogs - Keyboard Shortcuts part 2 (10 test cases) (#7112) * temporary work * further iteration * further work * Address CR comments Co-authored-by: Catalin Tomai Co-authored-by: Mattermod * [MM-29853] Migrate string-refs to functional ones: this.refs.fileInput (#7153) * Update string refs to functional component based refs * Update this.refs.fileInput to this.fileInputRef react ref function * Update string refs to functional component based refs * Update this.refs.fileInput to this.fileInputRef react ref function Co-authored-by: lestgabo * Fix display of boolean feature flags in system console. (#7156) * MM-30992 Add the capability to run part by part for Cypress parallel testing (part 1) (#7140) * add the capability to run part by part for Cypress parallel testing * update per comment and add default values * throw an error if --part is greater than --of * Cypress/E2E: Fix email verification that works both with current, Cloud and parallel testing (part 2) (#7142) * add the capability to run part by part for Cypress parallel testing * fix E2E email verification that works with cloud and parallel testing * add constants * update per comment * Cypress/E2E: Fix for several flaky tests (part 3) (#7143) * add the capability to run part by part for Cypress parallel testing * fix E2E email verification that works with cloud and parallel testing * add constants * fix flaky tests * Cypress/E2E: Fix tests for cloud (part 4) (#7147) * add the capability to run part by part for Cypress parallel testing * fix E2E email verification that works with cloud and parallel testing * add constants * fix flaky tests * fix tests for cloud * clean up label and flag * simplify log of license status and add log of server details * update per comment * fix return of Cypress custom commands (#7159) * [MM-30354] e2e fullname (#7126) Automatic Merge * Migrate to typescript Apps related files (#7021) * Migrate to typescript Apps related files * Use correct react-overlays types version * PR feedback * fix types * fix types * temporarily use any * remove unused import * Remove unneeded comments * Extract stringToNumber to utils * Fix minor warning on the console * Fix lint Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> * MM-30972 Add proper padding to code preview line numbers (#7155) Automatic Merge * MM-28412: Add tm4j key (#7171) * [MM-26593] Disable pointer events on LHS scrollbar when scrolling isn't required (#7120) Co-authored-by: Mattermod * Update NOTICE.txt (#7102) Co-authored-by: Mattermod * MM-30376 Cypress/E2E: Automate backlogs - Messaging (8 test cases) (#7141) Co-authored-by: Saturnino Abril Co-authored-by: Mattermod * [MM-30969] Drop aria-label from Marketplace item description (#7133) * Drop aria-label from Marketplace item description * Add general aria-label * Translations update from Weblate (#7182) * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.8% (4383 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.9% (4344 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.9% (4346 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Romanian) Currently translated at 100.0% (4391 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 100.0% (4391 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 100.0% (4393 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ Translated using Weblate (Chinese (Simplified)) Currently translated at 99.2% (4356 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ Translated using Weblate (Chinese (Simplified)) Currently translated at 99.1% (4356 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Dutch) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.2% (4359 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Japanese) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Translated using Weblate (Russian) Currently translated at 93.9% (4125 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ru/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4391 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ Co-authored-by: rodrigocorsi Co-authored-by: Viorel-Cosmin Miron Co-authored-by: aeomin Co-authored-by: Tom De Moor Co-authored-by: kaakaa Co-authored-by: Lev Co-authored-by: Kaya Zeren * MM-31115 - Fixed "how billing works" cloud link (#7183) * [MM-30372] Cypress/E2E: Automate backlogs - Messaging > Reply (#7149) * Implement Cypress test * Fix comments * Update e2e/cypress/integration/messaging/message_reply_part2_spec.js Co-authored-by: Saturnino Abril Co-authored-by: Saturnino Abril * MM-30374: Cypress/E2E: Messaging > Markdown (#7146) * MM-30374: Cypress/E2E: Messaging > Markdown This PR automates MM-T1734 MM-T2241 MM-T2242 MM-T2244 MM-T2246 https://mattermost.atlassian.net/browse/MM-30374 ```release-note NONE ``` * incorporate review comments Co-authored-by: Mattermod * MM-30810 Remove special props from /me post (#7163) * [MM-30449] - Hide create category option when unread filter is enabled (#7151) * [MM-30449] - Hide create category option when unread filter is enabled * Fix lint * Update snaps Co-authored-by: Nevyana Angelova * Add Billing Permissions (#7127) * Add billing permissions * add it to any * address comments * fix linting * address COMMENts! Co-authored-by: Mattermod * Remove usage of center-channel-color-## CSS variables from header components (#7118) Co-authored-by: Mattermod * MM-27688: OpenId Connect (#7027) * open id initial commit * fix for tests, remove force error * PR cleanup * fix upgrade gitlab * update redux * add cypress test, make fixes * add cypress, fix for cypress * add feature flag * fix linter errors * update redux * add feature flag to unit tests * fix linting * making configContains a one liner * fix test case and snapshot and linting * updates from code review * revert changes * update redux * uncomment feature flags check * remove unnecessary span * fix lint * clean up some code * update cypress tests * lint fixes * revert package-lock * revert package-lock * fix capitalization * update redux * update cypress tests to use findByRole * fix text differences from figma * remove feature flag * update cypress test * fix lint * updates from review * fix lint * Revert "fix lint" This reverts commit 3271dc7c2811d6e66bc1263cddd44906a5c73e52. * fix lint * fix lint * fix snapshots * fix bad merge * update redux Co-authored-by: Hossein Ahmadian-Yazdi Co-authored-by: Mattermod * Mm 30380/cypress dms and gms coverage (#7181) * Add DMs and GMs coverage as per MM-30380 * Fix linter * Apply PR suggestions * Fix linter complains * [MM-30390] - Add specs for team settings (#7105) * [MM-30390] - Add specs for team settings * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Resolve PR comments * resolve PR comments * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/team_settings/teams_spec.js Co-authored-by: Saturnino Abril * Resolve PR comments * resolve linter * Fix lint Co-authored-by: Nevyana Angelova Co-authored-by: Saturnino Abril Co-authored-by: Mattermod Co-authored-by: Nevyana Angelova * [MM-30358] - Add account position spec (#7108) * [MM-30358] - Add account position spec * Resolve PR comments * Resolve PR comments * Resolve failing test * fix linte Co-authored-by: Nevyana Angelova Co-authored-by: Nevyana Angelova * [MM-20487] Migrate 'components/post_view/post_time' module and associated tests to TypeScript (#6625) * [MM-29418] Apply the same sorting function (#15920) (#7128) * [MM-20432] Migrate components/channel_view module and associated tests to TypeScript (#6989) * [MM-27927] - Revert mattermost-redux (#7196) * [MM-30364] - Add spec for channel settings (#7111) Automatic Merge * Bump highlight.js from 10.1.2 to 10.4.1 (#7168) * Bump highlight.js from 10.1.2 to 10.4.1 Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 10.1.2 to 10.4.1. - [Release notes](https://github.com/highlightjs/highlight.js/releases) - [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md) - [Commits](https://github.com/highlightjs/highlight.js/compare/10.1.2...10.4.1) Signed-off-by: dependabot[bot] * Rename references from tex to latex Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mattermod Co-authored-by: Harrison Healey * [MM-29949] Add extra space for team name and user name on new sidebar (#6949) Co-authored-by: Mattermod * [MM-27213] Cypress/E2E: Automate backlogs - Auth - Part 01 (10 test cases) (#6860) * continue working on tests * cover more TESTSSSS * FINITO!!!! * fix linting * fix linting * update test * Address comments * use getRandomId * fix linting * authentication file in enterprise * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/system_console/authentication/authentication_spec.js Co-authored-by: Joseph Baylon * Fix linting * Address comments * some small fixes * Change to standard as much as possible Co-authored-by: Joseph Baylon Co-authored-by: Mattermod * [MM-30447] - Add create direct message option to category menu (#7152) * [MM-30447] - Add create direct message option to category menu * Update snaps * capitalise message * fix linter Co-authored-by: Nevyana Angelova Co-authored-by: Nevyana Angelova * [GH-16162] Migrate 'components/quick_switch_modal' module and associated tests to TypeScript (#7107) * [WIP] moved component to typescript * moved index & test to typescript * minor type update * minor type fixes Co-authored-by: Mattermod * [MM-28150] add remaining test cases (#7078) * e2e: add MM-T924, MM-T928, MM-T929, MM-T930 * reflect review comments * add remaining test cases * reflect reveiew comments * add timeouts * fix utility function * Update e2e/cypress/support/api/user.d.ts Co-authored-by: Saturnino Abril Co-authored-by: Mattermod Co-authored-by: Saturnino Abril * fix help text in tooltip (#6996) * small fix to typescript migration (#7170) Co-authored-by: Mattermod * MM-30373 - Cypress/E2E: Automate backlogs - Messaging > @ autocomplete (9 test cases) (#7066) * Bump ini from 1.3.5 to 1.3.7 (#7206) Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * MM-30381/cypress notifications teammate name MM-T488 (#7154) * Add MM-T488 Teammate name display set to user name on at mentions * Revert additional space between curly braces * Make linter happy * MM-30381/cypress notifications teammate name MM-T490 (#7158) * Add MM-T490 test case for MM-30381 desktop notifications * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js * Add MM-T489 test case for MM-30381 desktop notifications (#7157) * Fix linter Co-authored-by: Mattermod * [MM-30367] Automate E2E admin roles tests (#7162) Automatic Merge * Cypress/E2E: Automate backlogs - System Console > Compliance Export Downloads (#7187) * Covers MM-T3435, MM-T3438, MM-T3439 * Fixed lint errors * Requires license check Co-authored-by: Joseph Baylon * Wait until file storage page is visible Committed from suggestion Co-authored-by: Joseph Baylon * Wait until compliance export page is visible Committed from suggestion Co-authored-by: Joseph Baylon * Image upload wait code simplified Committed from suggestion Co-authored-by: Joseph Baylon * Remove images from post message Committed from suggestion Co-authored-by: Joseph Baylon * Comment formatted Committed from suggestion Co-authored-by: Joseph Baylon * Comment formatted Committed from suggestion Co-authored-by: Joseph Baylon * Comment formatted Committed from suggestion Co-authored-by: Joseph Baylon * Comment formatted Committed from suggestion Co-authored-by: Joseph Baylon * Comment formatted Committed from suggestion Co-authored-by: Joseph Baylon * Comment formatted Committed from suggestion Co-authored-by: Joseph Baylon * Wait until post message block is visible Committed from suggestion Co-authored-by: Joseph Baylon * Formatted comments * Replaced image upload wait check with initial code * Image upload code refactored, added timeout of 5 minutes * Moved to compliance dir * Uncommented tests which were commented by mistake * Update e2e/cypress/integration/enterprise/system_console/compliance/download_compliance_export_file_spec.js Co-authored-by: Joseph Baylon * Replaced .get with .findByTestId * Uncommented test cases Co-authored-by: Mattermod Co-authored-by: Joseph Baylon * [MM-31209] Take into account the OFF setting for OPEN ID (#7192) * account for off setting in open id * fix linting * [MM-16139] Migrate 'components/channel_header_dropdown/menu_items/toggle_favorite_channel' module and associated tests to TypeScript (#7110) Co-authored-by: Mattermod * MM-31275 New sidebar performance improvements (#7207) Automatic Merge * Update eslint-plugin-mattermost (#7214) * Fix linter issues found by @typscript-eslint/member-delimiter-style * Fix linter issues found by @typscript-eslint/type-annotation-spacing * Update eslint-plugin-mattermost * Translations update from Weblate (#7220) * Translated using Weblate (French) Currently translated at 93.2% (4096 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/fr/ * Translated using Weblate (Dutch) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.7% (4413 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.4% (4401 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.2% (4393 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.2% (4389 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Romanian) Currently translated at 100.0% (4424 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Spanish) Currently translated at 99.3% (4397 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Translated using Weblate (Spanish) Currently translated at 98.7% (4368 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Translated using Weblate (Spanish) Currently translated at 98.4% (4357 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.2% (4390 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4425 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Spanish) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Co-authored-by: wget Co-authored-by: Tom De Moor Co-authored-by: Viorel-Cosmin Miron Co-authored-by: Elias Nahum Co-authored-by: rodrigocorsi Co-authored-by: Kaya Zeren Co-authored-by: aeomin * [MM-27214] Cypress Automation Auth Numero Dos 2 (#7193) * write tests for authentication part 2 * finish tests part 2 * fix linting * fix liting and change to better fit standards * Update e2e/cypress/integration/enterprise/system_console/authentication/authentication2_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/system_console/authentication/authentication2_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/system_console/authentication/authentication2_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/system_console/authentication/authentication2_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/system_console/authentication/authentication2_spec.js * fix linting Co-authored-by: Joseph Baylon * MM-20405: Migrate configuration_bar module and tests to TypeScript (#6978) * MM-30394: Cypress/E2E - System Console > Settings (#7161) * MM-30394: Cypress/E2E - System Console > Settings This PR automates: MM-T953 MM-T1149 MM-T1161 MM-T1181 MM-T1635 ```release-note NONE ``` https://mattermost.atlassian.net/browse/MM-30394 * incorporate review comments * incorporate review comments * te check * Converted 'commands_container' to typescript. (#7121) * Converted 'commands_container' to typescript. * Fixing review comments. * Per review comments, added a 'TechDebt-TODO' for future changes from 'any' to 'Promise'. Co-authored-by: Mattermod * [MM-20436] Migrate toggle_mute_channel to TypeScript (#7138) * [MM-20436] Migrate toggle_mute_channel to TypeScript * Don't export Props * update the snapshot * fix lint errors Co-authored-by: Mattermod * [MM-27210] Archived channels 4 part I (#7191) Automatic Merge * [MM-31329] Enable @typescript-eslint/array-type (#7229) * Mm 30361 (#7190) * Update visit Function * Updated finding criteria * fixed suggested changes * Update e2e/cypress/integration/enterprise/ldap/ldap_setting_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/support/ldap_commands.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/ldap/ldap_setting_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/ldap/ldap_setting_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/ldap/ldap_setting_spec.js Co-authored-by: Joseph Baylon * Update e2e/cypress/integration/enterprise/ldap/ldap_setting_spec.js Co-authored-by: Joseph Baylon * fixed lint * Update e2e/cypress/integration/enterprise/ldap/ldap_setting_spec.js Co-authored-by: Scott Bishel Co-authored-by: Furqan Malik Co-authored-by: Furqan Malik Co-authored-by: abc Co-authored-by: Mattermod Co-authored-by: Joseph Baylon Co-authored-by: Scott Bishel * [MM-30159] Move multiselected channel to other categories via the 3-dot menu (#7123) * [MM-30159] Move multiselected channel to other categories via the 3-dot menu * Fixed a bug where you can't multi-select and move DMs into favourites * Optimization * Fixed an issue when clicking on an unselected normally that the selection wouldn't clear. * Type fix * Fixed move to new category to work with moving multiple channels * De-select channels upon moving to new category Co-authored-by: Mattermod * [MM-30360] Cypress/E2E: Account settings (#7185) * account settings tests * lint * lint 2 Co-authored-by: Mattermod * Allow uploading 10 files per post (#7160) Co-authored-by: Diogo Lima Nicolau Co-authored-by: Mattermod * Cypress/E2E: Automate backlogs - System Console > User Mgmt Deactivation (6 test cases) (#7203) * Cypress/E2E: Automate backlogs - System Console > User Mgmt Deactivation (6 test cases) * Enable old tests * Apply suggestions from code review * fix lint error Co-authored-by: Saturnino Abril Co-authored-by: Mattermod * [MM-31018] Update email address for admin advisor contact requests to support-advisor@mattermost.com (#7145) Automatic Merge * [MM-30383] - Desktop notifications spec (#7199) * [MM-30383] - Desktop notifications spec * fix linter * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Resolve comments and rebase * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Guillermo Vayá * Check if notification is called silently * Update e2e/cypress/integration/notifications/desktop_notifications_spec.js Co-authored-by: Saturnino Abril * Add back comment Co-authored-by: Nevyana Angelova Co-authored-by: Guillermo Vayá Co-authored-by: Saturnino Abril * Cypress/E2E: Update MM-T131 to include additional step/verification from archived MM-T2154 (#7186) * update the test to include verification of draft from duplicate test MM-T2150 * add aria-label to quick switch input and update the test with cy.findByRole * add translation string * revert unnecessary change to en.json * MM-T643 Incoming web-hook: Long URL for embedded image (#7099) * wip * fixes etc * - * ... * .. Co-authored-by: Mattermod * MM-31466 Performance investigation part two (#7242) * Improve typing performance in RHS * Add getCategoriesInCurrentTeam selector for use in SidebarChannelMenu * Re-use options passed into addLastViewAtToProfiles * Use getCategoriesForCurrentTeam everywhere * Memoize the collapsed state of categories * MM-30366: Adds test of group-mentioning a group outside of the channel's synced groups. (#7135) Automatic Merge * [MM-30379] add MM-T433, MM-T437 and MM-T438 (#7198) * add MM-T433, MM-T437 and MM-T438 * reflect review comments * use chains properly and some cleanup * [MM-27834] Collapse on more than 5 plugin buttons cypress tests (#6755) * Add collapse on more than 5 plugin buttons tests * Remove all plugins before test Co-authored-by: Mattermod * ensure it doesn't delete other permissions (#7233) * [MM-28259] custom slash commands e2e tests (#7129) * Add custom slash command e2e tests * Improve readability * Add comment Co-authored-by: Mattermod * [MM-30357] - Add nickname spec (#7216) * [MM-30357] - Add nickname spec * Update e2e/cypress/integration/account_settings/general/nickname_spec.js Co-authored-by: Guillermo Vayá * Resolve PR comments * Update e2e/cypress/integration/account_settings/general/nickname_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/account_settings/general/nickname_spec.js Co-authored-by: Saturnino Abril Co-authored-by: Nevyana Angelova Co-authored-by: Guillermo Vayá Co-authored-by: Saturnino Abril Co-authored-by: Mattermod * Cypress/E2E: Add custom commands for main menu, channel menu and account settings modal (#7218) * add custom commands for main menu, channel menu and account settings modal * update per comment * [MM-30369] Upload files tests (#7134) * Add tests for MM-30369 * Add file previews tests * Fix lint * Add group, add several waituntil, divide tests between failing and not failing, and rename tests * Change image.normal to image__thumbnail * [Cypress/E2E, MM-27222] Add test cases, fix makeClient, add/correct types (#7173) * fix client and its types * fix other types * add MM-T1812 and MM-T1813 * add MM-T1814 * comments/remove optional * refactor client maker, add remaining test cases * move to its own spec * fix header * fix import syntax causing eslint error * narrow find button scope * remove stray character * review changes * add check * Fix: Removed explicitly UTC-based timezone (#7114) * Update datetime.ts * Update datetime.ts Co-authored-by: Mattermod * [MM-27215] Cypress Authentication Part 3 Tests (#7204) Automatic Merge * [Cypress/E2E, MM-27224] Add Test Cases (#7255) * fix client and its types * fix other types * add MM-T1812 and MM-T1813 * add MM-T1814 * comments/remove optional * refactor client maker, add remaining test cases * move to its own spec * fix header * fix import syntax causing eslint error * narrow find button scope * remove stray character * add tags spec * review changes * wip * add check * wip * wip * wip * refactor, add tests * fix flaky test * wip * add MM-T1838 and MM-T1839 * same-code func * fix typo * typo-rename * Update e2e/cypress/integration/bot_accounts/tags_spec.js Co-authored-by: Joseph Baylon * Update tags_spec.js Co-authored-by: Joseph Baylon * [MM-20469] Transformed emoji_list_item to ts (#7137) Summary: Migrated components/emoji/emoji_list_item' module and associated tests to TypeScript Ticket Link: Fixes: mattermost/mattermost-server#15471 JIRA: https://mattermost.atlassian.net/browse/MM-20469 * add discard change pop up for add and remove users (#7236) Co-authored-by: Mattermod * MM-T444 DM More... show user count (#7087) * wip * refactor and remove step * comment fix * fixed describe desc * added save and new selectors * refactor and add additional assertion of font-weight * lint * wip * remove file * updated user number * etc * remove comment Co-authored-by: Mattermod * MM-27230 Cypress/E2E: Automate backlogs - BOT Accounts - Part 07 (10 test cases) (#7040) Co-authored-by: Saturnino Abril Co-authored-by: Prapti Co-authored-by: Mattermod * MM-29111: search bar and search results as functional components (#7029) Automatic Merge * MM-24072 - Position the autocomplete popover relative to the @, ~, or / trigger in the post draft (#7082) Automatic Merge * [MM-30337] - Fix mobile menus and remove redundant styles (#7226) * [MM-30337] - Fix mobile menus and remove redundant styles * Fix icon padding Co-authored-by: Nevyana Angelova Co-authored-by: Mattermod * Bump ini from 1.3.5 to 1.3.8 in /e2e (#7208) Automatic Merge * [MM-31149] don't allow change of roles if user can't manage members (#7200) Automatic Merge * [MM-30391] E2E tests - Team Settings pt. 2 (#7230) * [MM-30391] E2E tests - Team Settings pt. 2 * Uncomment test * PR feedback * Fix a typo Co-authored-by: Mattermod * Add check empty src strings (#7239) * Add check empty src strings * Update sha in lock file * Try * Update check-deps cache * Rm 2 empty korean translations * Fill empty source string Co-authored-by: Mattermod * [MM-27210] archived channels 4 II (#7205) * [MM-27210] search archive channels * move helper function * Migrate components/analytics/system_analytics module to TypeScript (#6997) Automatic Merge * MM-20449 Migrate 'components/integrations/installed_incoming_webhooks' module and associated tests to TypeScript (#7232) Automatic Merge * Clicking 'Jump to recent' in an archived channel should jump to recent messages (#7235) Co-authored-by: Mattermod * MM-20422 Migrate 'components/user_profile' module and associated tests to TypeScript (#7212) * Migrated user profile to typescript * Figured out a better way to deal with default props while using typescript * Moved userid from defaultprop to regular props * Updated snapshots with default props * Simplified extending definition for OverlayTrigger * Restricting props from makeMapStateToProps * Removing unnecessary type annotation * Simplified definition of default props type * Using semicolons to delimitate fields in type * updates coding style * Updated code styles (once again) Co-authored-by: Mattermod * Revert "MM-24072 - Position the autocomplete popover relative to the @, ~, or / trigger in the post draft (#7082)" (#7271) This reverts commit 7d7ebc77f483d0a79ae663c2d879f65f97153beb. * [MM-28155] Add link customization e2e tests (#7148) * Add link customization e2e tests * Change Ensure -> Verify Co-authored-by: Mattermod * Translations update from Weblate (#7273) * Translated using Weblate (German) Currently translated at 88.6% (3921 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/de/ * Translated using Weblate (Romanian) Currently translated at 100.0% (4426 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 100.0% (4425 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Korean) Currently translated at 78.3% (3469 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ Translated using Weblate (Korean) Currently translated at 78.3% (3468 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ * Translated using Weblate (Ukrainian) Currently translated at 72.0% (3190 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/uk/ Translated using Weblate (Ukrainian) Currently translated at 72.0% (3190 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/uk/ Translated using Weblate (Ukrainian) Currently translated at 72.0% (3189 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/uk/ * Translated using Weblate (German) Currently translated at 88.7% (3930 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/de/ * Translated using Weblate (Dutch) Currently translated at 99.9% (4425 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4426 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 99.9% (4425 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Spanish) Currently translated at 99.9% (4425 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Japanese) Currently translated at 99.9% (4423 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Japanese) Currently translated at 99.9% (4422 of 4423 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ Co-authored-by: Elisabeth Kulzer Co-authored-by: Viorel-Cosmin Miron Co-authored-by: Lee Dae-yeop Co-authored-by: Ivan Novikov Co-authored-by: AxicsHD Co-authored-by: Tom De Moor Co-authored-by: Kaya Zeren Co-authored-by: aeomin Co-authored-by: Elias Nahum Co-authored-by: kaakaa * [MM-30368] E2E Testing - Channel Sidebar (#7238) * [MM-30368] E2E Testing - Channel Sidebar * Lint fix * Update e2e/cypress/integration/channel_sidebar/dm_gm_behaviour_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/channel_sidebar/dm_gm_behaviour_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/channel_sidebar/dm_gm_behaviour_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/channel_sidebar/dm_gm_behaviour_spec.js Co-authored-by: Saturnino Abril * Update e2e/cypress/integration/channel_sidebar/dm_gm_behaviour_spec.js Co-authored-by: Saturnino Abril Co-authored-by: Saturnino Abril Co-authored-by: Mattermod * ignore uncaught exception by cypress (#7270) * demote unstabe tests, add flag when not to run on cloud and fix saving test report (#7269) * MM-20452 Migrate installed OAuth apps component to TypeScript (#7211) * Moved components/integrations/installed_oauth_app to typescript * Fixed comment indentation * Fixes after review * Testing what happens in CircleCI when removing trailing / from import `import InstalledOAuthApp from '../installed_oauth_app/';` -> `import InstalledOAuthApp from '../installed_oauth_app';` * Updated code style * Fixing code style Co-authored-by: Mattermod * [MM-4644] Upgrade Rate Limiting help text (#7277) * [MM-4644] Upgrade Rate Limiting help text Ticket link https://mattermost.atlassian.net/browse/MM-4644 Authored by: Max Erenberg * [GH-16211][MM-30274] Implement hotkeys for link markdown (#7125) * First draft of link-md-shortcut. * Add link markdown * Fix formatting issues. * Fix lint errors. * Implement suggested UX optimizations. * Handle the case of cursor inside word. * Fix export of internal methods. * Break apart methods for bold/italic and link markdowns. * Fix test issues * Handle end of line case. Co-authored-by: Mattermod * [MM-20467] Migrate 'components/emoji/emoji_list' module and associated tests to TypeScript Summary: Migrates 'components/emoji/emoji_list' module and associated tests to TypeScript Ticket Link: Fixes: mattermost/mattermost-server#15473 Jira: https://mattermost.atlassian.net/browse/MM-20467 * MM-31361: Fix missing error text when team name is empty (#7241) Summary Leaving the team name field blank when creating a new team should cause error text to display. The error text was no longer displaying, and there is a JS error in the console. This is caused by empty team being passed in the component props. Ticket Link https://mattermost.atlassian.net/browse/MM-31361 * MM-31697- Remove OpenId from System Console (#7278) * remove OpenId from System Console * remove openid cypress tests * update unit tests * [MM-30384] Implement Cypress test cases (#7265) * Implement Cypress test cases * Update snapshot * Address PR comments * [MM-30349] Demo plugin - Webhook events (#7197) * Add demo plugin webhook e2e tests * Address review suggestions Co-authored-by: Mattermod * Bump axios from 0.20.0 to 0.21.1 in /e2e (#7272) Bumps [axios](https://github.com/axios/axios) from 0.20.0 to 0.21.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v0.21.1/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v0.20.0...v0.21.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * MM-31189 Revert New Messages toast PRs (#7292) * Revert "MM-29929 Fix for unread messages toast on marking channel as unread (#7028)" This reverts commit 92f5d86dd092d1495a98ce9297cea7be88f5f6fe. * Revert "[MM-24436]- Add a threshold from bottom for new messages toast (#5828)" This reverts commit 855633df1f2f4526a4a4aa40ee166fe52fe8e184. * [MM-31580] Fix call to getChannel to use new parameter format (#7284) * [MM-31708][MM-31726][MM-31739][MM-31727] - Fix regressions in RHS and Status menu (#7290) * Fix regressions in RHS and Status menu * update tests * Fix separator margins Co-authored-by: Nevyana Angelova * MM-31858 - Include build hash in cloud about modal (#7303) * MM-31716: Remove the word experimental from gossip setting (#7287) This PR just makes a small UI adjustment which removes the word experimental from the system console. Any other things like variable names and id names haven't been touched because the entire thing is going to be removed after 3 months when the feature becomes default. Test snapshots have been updated, and verified that there are no Cypress tests matching this string. https://mattermost.atlassian.net/browse/MM-31716 ```release-notes Removed the word "experimental" from gossip setting in the admin console ``` * add explicit wait with cy.postMessage (#7280) * [MM-30352] Cypress/E2E: Plugin Management (#7268) Automatic Merge * Convert 'components/about_build_modal/about_build_modal' to typescript (#7223) * Convert 'components/about_build_modal/about_build_modal' to typescript. * Fixing review comments. * Fixing review comments. * Fixing review comments. * Fixing review comments. Co-authored-by: Mattermod * adding Swedish and Bulgarian translations (#7289) * Added translation using Weblate (Swedish) * Added translation using Weblate (Bulgarian) * Translated using Weblate (Swedish) Currently translated at 100.0% (4423 of 4423 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/sv/ * Translated using Weblate (Bulgarian) Currently translated at 99.9% (4422 of 4423 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/bg/ * [MM-4644] Upgrade Rate Limiting help text (#7277) * [MM-4644] Upgrade Rate Limiting help text Ticket link https://mattermost.atlassian.net/browse/MM-4644 Authored by: Max Erenberg * [GH-16211][MM-30274] Implement hotkeys for link markdown (#7125) * First draft of link-md-shortcut. * Add link markdown * Fix formatting issues. * Fix lint errors. * Implement suggested UX optimizations. * Handle the case of cursor inside word. * Fix export of internal methods. * Break apart methods for bold/italic and link markdowns. * Fix test issues * Handle end of line case. Co-authored-by: Mattermod * adding Swedish and Bulgarian translations * modified the order of languages * Update i18n/i18n.jsx * Updated snapshot Co-authored-by: Tom De Moor Co-authored-by: Weblate Co-authored-by: majo Co-authored-by: Nikolai Zahariev Co-authored-by: Hosted Weblate Co-authored-by: maxerenberg <34190303+maxerenberg@users.noreply.github.com> Co-authored-by: Jyoti Patel <36148363+jp0707@users.noreply.github.com> Co-authored-by: Mattermod Co-authored-by: Elisabeth Kulzer * Fix limit banner show modal for cloud in-app purchases (#7305) * change super to system (#7076) (#7088) Automatic Merge * MM-20465 Fix for collapse/expand of image preview (#7085) * The toggle function requires id and not post * This was most likely changed to fix a ts error * MM-20465 Revert usage of getCurrentChannel to getCurrentChannelId where possible (#7064) (#7092) (cherry picked from commit 1d7e5e983debaba04883507bccd959a9001c2cff) Co-authored-by: Harrison Healey * Automated cherry pick of #7096 (#7097) (cherry picked from commit 6c35b79ac663756b5e31f0b878531aabec68efd3) Co-authored-by: Maria A Nunez (cherry picked from commit 59cebceb669dec97fc539a3189ebc3eb94966efd) * Automated cherry pick of #7101 (#7104) (cherry picked from commit 0595ff31d9253b13ae6aa973fcb391f47b72f8cf) Co-authored-by: Nick Misasi * MM-30477 Fix for autocomplete not closing (#7093) Automatic Merge * Add GitLab jobs * Translations update from Weblate (#7122) * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Romanian) Currently translated at 93.8% (4121 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 98.9% (4345 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4392 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Spanish) Currently translated at 97.7% (4292 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Dutch) Currently translated at 98.8% (4341 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Co-authored-by: Viorel-Cosmin Miron Co-authored-by: aeomin Co-authored-by: Kaya Zeren Co-authored-by: Elias Nahum Co-authored-by: Tom De Moor * Translations update from Weblate (#7139) * Translated using Weblate (Spanish) Currently translated at 99.1% (4357 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Dutch) Currently translated at 98.8% (4344 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Dutch) Currently translated at 98.9% (4349 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.4% (4327 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Co-authored-by: Elias Nahum Co-authored-by: Tom De Moor Co-authored-by: rodrigocorsi * Merge latest master into cloud branch for next release (#7144) * DOPS-243 (#7075) DOPS-243 (#7075) * [GH-13483] Migrate 'components/channel_invite_modal' module and associated tests to TypeScript (#6968) * [WIP] migration to typescript * [WIP] further typescript changes * finalized move of component to typescript * [WIP] move index file to typescript * updated test to typescript * fix for types in index file * removed failing class import * [No Ticket] Update to latest redux hash and fixing test cases with missing types (#7084) Automatic Merge * [MM-27154] Cypress tests: Incoming Webhooks (#6977) * Incoming webhooks cypress tests * Remove unused variable * PR feedback * add license * PR feedback and lint * fix import path Co-authored-by: Saturnino Abril Co-authored-by: Mattermod Co-authored-by: Saturnino Abril * fix failing tests due to announcement bar (#7077) * MM-27315 Cypress tests for Integrations > Slash command auto-complete (7 test cases) (#6909) * MM-30359: Cypress/E2E: Account Settings > Email (#7051) * MM-30359: Cypress/E2E: Account Settings > Email This PR automates the following test cases: MM-T2066 MM-T2067 MM-T2068 MM-T2069 MM-T2070 MM-T2071 MM-T2072 MM-T2073 https://mattermost.atlassian.net/browse/MM-30359 ```release-note NONE ``` * fix lint * add the other tests * Address review comments Co-authored-by: Mattermod * [MM-T561] Add e2e test for MM-T561 (#7015) * Add e2e test Add test for: Browser tab and team sidebar - direct messages don't add indicator on team icon in team sidebar (but do in browser tab) Github Issues: Fix https://github.com/mattermost/mattermost-server/issues/15700 Jira Ticket: MM-T561 * Apply suggested review * Add cleanup for MM-T560_2 * Linting * Separate test file * Linting * Add prerequisite * Rename file * Remove old test Co-authored-by: Mattermod * [MM-28255] Add tests for OAuth Apps (#6558) * Add tests for OAuth Apps * Add remaining tests * Update test names, fix typos and add explicit status codes * Add group and license check * Move tests to enterprise folder Co-authored-by: Mattermod * MM-29858: Migrate string refs to functional ones in autosize textarea component (#6885) * MM-29858: Migrate string refs to functional ones in autosize textarea component * MM-29858: Rename reference instance variable with suffix Ref * MM-29858: Use camel casing for textarea reference variable * MM-29858: Allow empty value for textarea Co-authored-by: Mattermod * Migrate errorMessage string ref (#6882) Automatic Merge * MM-T329 Image link preview (#7005) * wip * wip * finished * revision * remove log Co-authored-by: Mattermod * [MM-28150] e2e: add MM-T924, MM-T928, MM-T929, MM-T930 (#7045) * e2e: add MM-T924, MM-T928, MM-T929, MM-T930 * reflect review comments Co-authored-by: Mattermod * MM-30363: Cypress/E2E: Automate backlogs - Edit Bot Username (1 test case) (#7073) * MM-30373 - first iteration * MM-30377 - first iteration * remove spurious file * MM-30377 - further iteration * MM-30363 - first iteration * remove extra file * some cleanup * remoe extra space Co-authored-by: Catalin Tomai Co-authored-by: Mattermod * promote and demote tests to/from prod (#7080) * Cypress/E2E: Fix toast appears unread spec (#7081) * change super to system (#7076) * MM-20418: Migrate 'components/channel_selector_modal' module and associated tests to TypeScript (#6975) * DOPS-243: Fix images (#7089) DOPS-243: Fix images (#7089) * MM-T439 Town Square is not marked as unread for existing users when a new user is added to the team (#7053) Automatic Merge * [MM-20582] Migrate 'components/admin_console/team_channel_settings/ch… (#6840) * MM-20465 Fix for collapse/expand of image preview (#7085) * The toggle function requires id and not post * This was most likely changed to fix a ts error * MM-20465 Revert usage of getCurrentChannel to getCurrentChannelId where possible (#7064) * [MM-29821] Prefer TypeScript files over JavaScript in webpack (#7090) * [MM-30317] add MANAGE_REMOTE_CLUSTERS permission (#7060) * add MANAGE_REMOTE_CLUSTERS permission * Update i18n/en.json Co-authored-by: Doug Lauder Co-authored-by: Doug Lauder * MM-20416: Migrate 'components/channel_header_mobile/collapse_lhs_button' module to TypeScript (#6971) Co-authored-by: Mattermod * Remove Cloud billing flag from System Console (#7096) (cherry picked from commit 6c35b79ac663756b5e31f0b878531aabec68efd3) Co-authored-by: Maria A Nunez * Translations update from Weblate (#7100) * Translated using Weblate (Turkish) Currently translated at 100.0% (4390 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ Translated using Weblate (Turkish) Currently translated at 100.0% (4329 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (German) Currently translated at 90.4% (3915 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/de/ * Translated using Weblate (Korean) Currently translated at 80.1% (3468 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (4328 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Russian) Currently translated at 95.0% (4113 of 4329 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ru/ * Translated using Weblate (Dutch) Currently translated at 98.5% (4328 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 98.4% (4322 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 96.7% (4246 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Romanian) Currently translated at 93.8% (4119 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 92.7% (4072 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Dutch) Currently translated at 98.5% (4325 of 4390 strings) Translation: mattermost-languages-shipped/mattermost-webapp_master Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Co-authored-by: Kaya Zeren Co-authored-by: Elisabeth Kulzer Co-authored-by: Ji-Hyeon Gim Co-authored-by: rodrigocorsi Co-authored-by: Alexey Napalkov Co-authored-by: Tom De Moor Co-authored-by: aeomin Co-authored-by: Viorel-Cosmin Miron * Migrate 'components/announcement_bar/default_announcement_bar' module and associated tests to TypeScript (#6963) * rename file * working except for test file * restore unincluded test file to jsx * pass npm make check * update snapshot * respond to review comments * checkout package-lock.json * revert package-lock.json changes * fix linting errors Co-authored-by: Mattermod * Migrate this.ref.dotMenu to functional (#6957) * Migrate this.ref.dotMenu to functional * Fixed formatting * Fix reference * rename dotMenu to dotMenuRef for consistency with other PRs, remove comments * Removed blanked line to pass lint check Co-authored-by: root Co-authored-by: Mattermod * MM-20897 Add category muting (#7033) * MM-20897 Add ability to mute categories * Increase timeout to ensure test passes * Add E2E test for moving channels into muted categories * Update mattermost-redux to branch * Fix unit tests * Fix types * Update snapshot * asdf * Add additional E2E test and API client infrastructure * MM-24293 Fix to have group channels return in quick switcher with space (#7059) * MM-24293 Fix to have group channels return in quick siwchter with space * Split the search term by spaces and match with users in group channels * Update components/suggestion/switch_channel_provider.jsx Co-authored-by: Guillermo Vayá Co-authored-by: Guillermo Vayá Co-authored-by: Mattermod * If card year is 0 return false for isExpired (#7101) * Converted 'dot_menu' to typescript. (#6825) * Migrate string refs of reset password modal (#7038) Automatic Merge * MM-20415 Migrate 'components/channel_header_mobile/unmute_channel_button' module and associated tests to TypeScript (#6972) * MM-30477 Fix for autocomplete not closing (#7093) Automatic Merge * MM-20897 Cleanup a couple missed issues (#7106) * MM-T638 Webhook posts when webhook creator is not a member of the channel (#7091) * wip * wip * wip * wip * added check * small fix * fix Co-authored-by: Mattermod * [MM-20481] Migrate 'components/post_view/post_body_additional_content' module and associated tests to TypeScript (#6668) * [MM-20481] Migrate 'components/post_view/post_body_additional_content' module and associated tests to TypeScript Fixes https://github.com/mattermost/mattermost-server/issues/15460 JIRA: https://mattermost.atlassian.net/browse/MM-20481 * Make actions attribute non nullable * Require YoutubeVideo's postId property since it really is * Fix tests * Add stages for onprem * Add build-vars stage * set isDisabled prop for site > notices (#7103) Automatic Merge * [MM-27931][MM-30158] Multi-selection and dragging of channels (#6979) * [MM-27931] Multiselection of channels * Test fix * Lint fix * Change opacity of selected state to 0.24 * Added hover state for selected channels * Tests for multiSelect * Drag and drop working (without visuals, missing redux commit) * Multi drag and drop styles (fade the selected channels, might change) * Don't allow multisellected channels to be dropped where they shouldn't be * Lint, type, test, translation fixes * Allow dragging of non matching channel groups by rejecting the channels that don't match * Redux update * Another test fix * Fixed selected count style to work off of center channel exclusively * Added really obvious styling * Style change/PR feedback * Style change * Include active channel in all initial selections * PR feedback * Add constant for shifted index Co-authored-by: Mattermod * [MM-20572] Migrate 'components/admin_console/compliance_reports' to TypeScript (#13500) (#7036) Automatic Merge * MM-30087 Remove direct dependency between Client4 and Rudder (#7056) * MM-30087 Remove direct dependency between Client4 and Rudder * Update tests * Switch mattermost-redux to master * Fix missing types * Change how we mock and import rudder * Remove test that checks if events are sent to Rudder * [MM_20423]'external_image'module to ts (#16151) (#7025) - migrate ts index.js file - migrate ts for external_image.tsx - migrate ts for external_images.test.jsx Co-authored-by: Hossein Ahmadian-Yazdi * Fix issue 16144: migrate components/claim module and tests to TypeScript (#7043) Automatic Merge * MM-T2056 e2e to test, Username change reflecting for other users (#7074) Co-authored-by: Mattermod * [MM-26334] Fixes direct messages loading screen misalignment (#6813) Summary: Reduces the height of the loading screen so it is more centered and/or unintrusive(small screens) enough to remove the scroll bar during loading. Ticket Link: Fixes https://github.com/mattermost/mattermost-server/issues/15926 * Translations update from Weblate (#7122) * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Romanian) Currently translated at 93.8% (4121 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 98.9% (4345 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4392 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Spanish) Currently translated at 97.7% (4292 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Dutch) Currently translated at 98.8% (4341 of 4392 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Co-authored-by: Viorel-Cosmin Miron Co-authored-by: aeomin Co-authored-by: Kaya Zeren Co-authored-by: Elias Nahum Co-authored-by: Tom De Moor * MM-28153: Cypress/E2E: Automate backlogs - System Console > Environment (8 test cases) (#7049) * MM-30377: Cypress/E2E: Automate backlogs - Messaging > Channel and Post Links (6 test cases) (#7072) * MM-30355: Cypress/E2E: Automate backlogs - Account Settings > Username (6 test cases) (#7062) * MM-27208 Add tests for MM-T1684, MM-T1687, and MM-T1688 (#7124) * MM-27208 MM-T1684 Add test number to existing test case * Fix makeClient for requests without a body * MM-27208 MM-T1687 Add test for another user archiving a channel * MM-27208 MM-T1688 Add test for searching in archived channels * Translations update from Weblate (#7139) * Translated using Weblate (Spanish) Currently translated at 99.1% (4357 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Dutch) Currently translated at 98.8% (4344 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Dutch) Currently translated at 98.9% (4349 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.4% (4327 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Co-authored-by: Elias Nahum Co-authored-by: Tom De Moor Co-authored-by: rodrigocorsi Co-authored-by: Elisabeth Kulzer Co-authored-by: Tobias Weichart Co-authored-by: Hossein Ahmadian-Yazdi Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> Co-authored-by: Mattermod Co-authored-by: Saturnino Abril Co-authored-by: Jason Frerich Co-authored-by: Agniva De Sarker Co-authored-by: seongwon-kang <56567660+seongwon-kang@users.noreply.github.com> Co-authored-by: Daniel Espino García Co-authored-by: sowmiyamuthuraman <32141844+sowmiyamuthuraman@users.noreply.github.com> Co-authored-by: Sridhar Co-authored-by: Michael Leonard Co-authored-by: Ibrahim Serdar Acikgoz Co-authored-by: catalintomai <56169943+catalintomai@users.noreply.github.com> Co-authored-by: Catalin Tomai Co-authored-by: Joseph Baylon Co-authored-by: Revanth M <7revanth47@gmail.com> Co-authored-by: Clément Collin Co-authored-by: Sudheer Co-authored-by: Harrison Healey Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Co-authored-by: Doug Lauder Co-authored-by: Mattermost Build Co-authored-by: Maria A Nunez Co-authored-by: Weblate (bot) Co-authored-by: Kaya Zeren Co-authored-by: Elisabeth Kulzer Co-authored-by: Ji-Hyeon Gim Co-authored-by: rodrigocorsi Co-authored-by: Alexey Napalkov Co-authored-by: Tom De Moor Co-authored-by: aeomin Co-authored-by: Viorel-Cosmin Miron Co-authored-by: Morgan Connolly Co-authored-by: Ekaterina Grinberg <46496977+egrinberg@users.noreply.github.com> Co-authored-by: root Co-authored-by: Guillermo Vayá Co-authored-by: Nick Misasi Co-authored-by: Vijay Raghavan Aravamudhan Co-authored-by: Nicolas Le Cam Co-authored-by: Daniel Shuy Co-authored-by: dizkek <41262168+dizkek@users.noreply.github.com> Co-authored-by: Hossein Ahmadian-Yazdi Co-authored-by: Luciano Lim Co-authored-by: Elias Nahum * MM-30972 Add proper padding to code preview line numbers (#7155) (#7167) Automatic Merge * Automated cherry pick of #7133 (#7178) * Drop aria-label from Marketplace item description * Add general aria-label (cherry picked from commit 01b80cabcfb8fc6155139e2d243abdaf5010cd99) Co-authored-by: Ben Schumacher * Update NOTICE.txt (#7102) (#7179) Automatic Merge * Automated cherry pick of #7183 (#7184) (cherry picked from commit 22793b4cb49d3114a5ff2330370cef62af22f46f) Co-authored-by: Maria A Nunez * Translations update from Weblate (#7182) * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.8% (4383 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.9% (4344 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.9% (4346 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Romanian) Currently translated at 100.0% (4391 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 100.0% (4391 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 100.0% (4393 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ Translated using Weblate (Chinese (Simplified)) Currently translated at 99.2% (4356 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ Translated using Weblate (Chinese (Simplified)) Currently translated at 99.1% (4356 of 4393 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Dutch) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.2% (4359 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Japanese) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Translated using Weblate (Russian) Currently translated at 93.9% (4125 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ru/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4391 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ Co-authored-by: rodrigocorsi Co-authored-by: Viorel-Cosmin Miron Co-authored-by: aeomin Co-authored-by: Tom De Moor Co-authored-by: kaakaa Co-authored-by: Lev Co-authored-by: Kaya Zeren * Translations update from Weblate (#7220) * Translated using Weblate (French) Currently translated at 93.2% (4096 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/fr/ * Translated using Weblate (Dutch) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.7% (4413 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.4% (4401 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.2% (4393 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.2% (4389 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ Translated using Weblate (Dutch) Currently translated at 99.9% (4390 of 4391 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Romanian) Currently translated at 100.0% (4424 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Spanish) Currently translated at 99.3% (4397 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Translated using Weblate (Spanish) Currently translated at 98.7% (4368 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Translated using Weblate (Spanish) Currently translated at 98.4% (4357 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.2% (4390 of 4424 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/pt_BR/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4425 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Spanish) Currently translated at 99.9% (4424 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ Co-authored-by: wget Co-authored-by: Tom De Moor Co-authored-by: Viorel-Cosmin Miron Co-authored-by: Elias Nahum Co-authored-by: rodrigocorsi Co-authored-by: Kaya Zeren Co-authored-by: aeomin * MM-31275 New sidebar performance improvements (#7207) (#7240) Automatic Merge * MM-31466 Performance investigation part two (cloud) (#7245) Automatic Merge * Translations update from Weblate (#7273) * Translated using Weblate (German) Currently translated at 88.6% (3921 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/de/ * Translated using Weblate (Romanian) Currently translated at 100.0% (4426 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ Translated using Weblate (Romanian) Currently translated at 100.0% (4425 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ro/ * Translated using Weblate (Korean) Currently translated at 78.3% (3469 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ Translated using Weblate (Korean) Currently translated at 78.3% (3468 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ko/ * Translated using Weblate (Ukrainian) Currently translated at 72.0% (3190 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/uk/ Translated using Weblate (Ukrainian) Currently translated at 72.0% (3190 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/uk/ Translated using Weblate (Ukrainian) Currently translated at 72.0% (3189 of 4425 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/uk/ * Translated using Weblate (German) Currently translated at 88.7% (3930 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/de/ * Translated using Weblate (Dutch) Currently translated at 99.9% (4425 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/nl/ * Translated using Weblate (Turkish) Currently translated at 100.0% (4426 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/tr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 99.9% (4425 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/zh_Hans/ * Translated using Weblate (Spanish) Currently translated at 99.9% (4425 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/es/ * Translated using Weblate (Japanese) Currently translated at 99.9% (4423 of 4426 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ * Translated using Weblate (Japanese) Currently translated at 99.9% (4422 of 4423 strings) Translation: mattermost-languages-shipped/mattermost-webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-webapp_master/ja/ Co-authored-by: Elisabeth Kulzer Co-authored-by: Viorel-Cosmin Miron Co-authored-by: Lee Dae-yeop Co-authored-by: Ivan Novikov Co-authored-by: AxicsHD Co-authored-by: Tom De Moor Co-authored-by: Kaya Zeren Co-authored-by: aeomin Co-authored-by: Elias Nahum Co-authored-by: kaakaa * MM-31697- Remove OpenId from System Console (#7278) (#7294) Automatic Merge * [MM-31580] Fix call to getChannel to use new parameter format (#7284) (#7300) (cherry picked from commit 77a9c3cefade1d1195e8cb6997484fa7370dc622) Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> * MM-31189 Revert New Messages toast PRs (#7292) (#7301) Automatic Merge * [MM-31708][MM-31726][MM-31739][MM-31727] - Fix regressions in RHS and Status menu (#7290) (#7302) Automatic Merge * Add onClick event to button so banner button works for cloud * Change to onButtonClick * Reset changes for package-lock * Put showModal back in case its used elsewhere * Fix invocation * Fix payment_announcement_bar * Reset 2 files to master Co-authored-by: Mattermost Build Co-authored-by: Sudheer Co-authored-by: Harrison Healey Co-authored-by: Elisabeth Kulzer Co-authored-by: Weblate (bot) Co-authored-by: Viorel-Cosmin Miron Co-authored-by: aeomin Co-authored-by: Kaya Zeren Co-authored-by: Elias Nahum Co-authored-by: Tom De Moor Co-authored-by: rodrigocorsi Co-authored-by: Joram Wilander Co-authored-by: Tobias Weichart Co-authored-by: Hossein Ahmadian-Yazdi Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> Co-authored-by: Mattermod Co-authored-by: Saturnino Abril Co-authored-by: Jason Frerich Co-authored-by: Agniva De Sarker Co-authored-by: seongwon-kang <56567660+seongwon-kang@users.noreply.github.com> Co-authored-by: Daniel Espino García Co-authored-by: sowmiyamuthuraman <32141844+sowmiyamuthuraman@users.noreply.github.com> Co-authored-by: Sridhar Co-authored-by: Michael Leonard Co-authored-by: Ibrahim Serdar Acikgoz Co-authored-by: catalintomai <56169943+catalintomai@users.noreply.github.com> Co-authored-by: Catalin Tomai Co-authored-by: Joseph Baylon Co-authored-by: Revanth M <7revanth47@gmail.com> Co-authored-by: Clément Collin Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Co-authored-by: Doug Lauder Co-authored-by: Maria A Nunez Co-authored-by: Elisabeth Kulzer Co-authored-by: Ji-Hyeon Gim Co-authored-by: Alexey Napalkov Co-authored-by: Morgan Connolly Co-authored-by: Ekaterina Grinberg <46496977+egrinberg@users.noreply.github.com> Co-authored-by: root Co-authored-by: Guillermo Vayá Co-authored-by: Vijay Raghavan Aravamudhan Co-authored-by: Nicolas Le Cam Co-authored-by: Daniel Shuy Co-authored-by: dizkek <41262168+dizkek@users.noreply.github.com> Co-authored-by: Hossein Ahmadian-Yazdi Co-authored-by: Luciano Lim Co-authored-by: Ben Schumacher Co-authored-by: kaakaa Co-authored-by: Lev Co-authored-by: wget Co-authored-by: Lee Dae-yeop Co-authored-by: Ivan Novikov Co-authored-by: AxicsHD * Remove background color for post action buttons (#6926) * Remove background color for post action buttons * Fix test Co-authored-by: Mattermod * [GH-16126] - Migrate 'components/integrations/edit_command' module and associated tests to TypeScript (#7251) * Migrate components/integrations/edit_command redux hoc to ts * Migrate components/integrations/edit_command component to ts * Convert edit_command test files to tsx and fix issues with getCustomTeamCommands action and command create_at type inconsistency * Create a helper func for mocking commands in utils/test_helper.ts and use getCommandMock in edit_command test file * Fix failing tests in components/integrations/edit_command/edit_command.test.tsx and update snapshot to match the team object * Update snapshop edit_command.test.tsx.snap to have team properties in alphabetical order * Function getCustomTeamCommands should return an array of Command objects Co-authored-by: Mattermod * Some messages mark as traslatable (#7266) * Mark some messages in purchase_modal as translatable * Mark some messages in next_steps_tips as traslatable * fix message text * Fix some lint and type errors Co-authored-by: Sven Hüster Co-authored-by: Mattermod Co-authored-by: Nev Angelova Co-authored-by: Nick Misasi Co-authored-by: Weblate (bot) Co-authored-by: Tom De Moor Co-authored-by: Elias Nahum Co-authored-by: kaakaa Co-authored-by: Kaya Zeren Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Co-authored-by: vanya829 Co-authored-by: i.nikolaievskyi Co-authored-by: Clément Collin Co-authored-by: Christopher Speller Co-authored-by: Lestley Gabo Co-authored-by: seongwon-kang <56567660+seongwon-kang@users.noreply.github.com> Co-authored-by: Saturnino Abril Co-authored-by: Pijus Kamandulis Co-authored-by: Joseph Baylon Co-authored-by: Nevyana Angelova Co-authored-by: Takuro <36841033+Tak-Iwamoto@users.noreply.github.com> Co-authored-by: Nev Angelova Co-authored-by: Nevyana Angelova Co-authored-by: Michael Leonard Co-authored-by: Mattermost Co-authored-by: Vijay Raghavan Aravamudhan Co-authored-by: Harrison Healey Co-authored-by: Caleb Roseland Co-authored-by: VolatianaYuliana Co-authored-by: Sridhar Co-authored-by: Sudheer Co-authored-by: Prithvijit Dasgupta <55779009+prithvijit-dasgupta@users.noreply.github.com> Co-authored-by: Matt Spotts Co-authored-by: Hossein Ahmadian-Yazdi Co-authored-by: Nevyana Angelova Co-authored-by: rodrigocorsi Co-authored-by: Ji-Hyeon Gim Co-authored-by: Ibrahim Serdar Acikgoz Co-authored-by: Shinn Lok Co-authored-by: Pablo Andrés Vélez Vidal Co-authored-by: Pablo Velez Co-authored-by: Ashish Bhate Co-authored-by: Ben Schumacher Co-authored-by: Farhan Munshi Co-authored-by: Hossein Ahmadian-Yazdi Co-authored-by: Saucistophe Co-authored-by: Christophe Carpentier Co-authored-by: Revanth M <7revanth47@gmail.com> Co-authored-by: Junyong Liu <60892464+tianlangwu@users.noreply.github.com> Co-authored-by: Eli Yukelzon Co-authored-by: Prapti Co-authored-by: Amarpreet Singh Co-authored-by: Elisabeth Kulzer Co-authored-by: Tobias Weichart Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> Co-authored-by: Jason Frerich Co-authored-by: Agniva De Sarker Co-authored-by: sowmiyamuthuraman <32141844+sowmiyamuthuraman@users.noreply.github.com> Co-authored-by: catalintomai <56169943+catalintomai@users.noreply.github.com> Co-authored-by: Catalin Tomai Co-authored-by: Doug Lauder Co-authored-by: Mattermost Build Co-authored-by: Maria A Nunez Co-authored-by: Elisabeth Kulzer Co-authored-by: Alexey Napalkov Co-authored-by: aeomin Co-authored-by: Viorel-Cosmin Miron Co-authored-by: Morgan Connolly Co-authored-by: Ekaterina Grinberg <46496977+egrinberg@users.noreply.github.com> Co-authored-by: root Co-authored-by: Guillermo Vayá Co-authored-by: Nicolas Le Cam Co-authored-by: Daniel Shuy Co-authored-by: dizkek <41262168+dizkek@users.noreply.github.com> Co-authored-by: Luciano Lim Co-authored-by: Ayan Banerjee Co-authored-by: Kevin Mario Gerard <30295168+KevinMarioGerard@users.noreply.github.com> Co-authored-by: Guillermo Vayá Co-authored-by: Amy Blais Co-authored-by: Lev Co-authored-by: Claudio Costa Co-authored-by: Haardik Dharma Co-authored-by: Scott Bishel Co-authored-by: John Tzikas Co-authored-by: Jakub Nowak <30349303+jakubnovak998@users.noreply.github.com> Co-authored-by: Shashwat Bagaria Co-authored-by: KC <31262826+kcc343@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yusuke Nemoto Co-authored-by: Anurag Shivarathri Co-authored-by: Clément Yam <34475183+Tzunhei@users.noreply.github.com> Co-authored-by: wget Co-authored-by: Scott Hardin Co-authored-by: Mohammed Salman Co-authored-by: Malik Co-authored-by: Furqan Malik Co-authored-by: Furqan Malik Co-authored-by: abc Co-authored-by: Diogo Nicolau Co-authored-by: Diogo Lima Nicolau Co-authored-by: Jason Blais <13119842+jasonblais@users.noreply.github.com> Co-authored-by: Martin Kraft Co-authored-by: Shota Gvinepadze Co-authored-by: Caleb Roseland Co-authored-by: Michel Engelen <32863416+michelengelen@users.noreply.github.com> Co-authored-by: Pablo Andrés Vélez Vidal Co-authored-by: nikkinagar <6557942+nikkinagar@users.noreply.github.com> Co-authored-by: Luciano Mammino Co-authored-by: Lee Dae-yeop Co-authored-by: Ivan Novikov Co-authored-by: AxicsHD Co-authored-by: maxerenberg <34190303+maxerenberg@users.noreply.github.com> Co-authored-by: Jyoti Patel <36148363+jp0707@users.noreply.github.com> Co-authored-by: ctlaltdieliet Co-authored-by: Weblate Co-authored-by: majo Co-authored-by: Nikolai Zahariev Co-authored-by: Joram Wilander Co-authored-by: Lucie <46979603+lucievr@users.noreply.github.com> commit 1ad4c4a2d1c0db215417513c8df2a229190dfa4d Author: Daniel Espino García Date: Tue Dec 15 16:17:24 2020 +0100 Add text subtypes (#7150) * Add text subtypes * Fix lint commit 17f08e1efb6f552d5315ac45936cdaf49ff924c6 Author: Daniel Espino García Date: Fri Dec 4 16:57:35 2020 +0100 Embedded forms (#6804) * First approach on embedded forms * Fix lint * Render form in the RHS * Send needed information along the form * Address feedback * Use proper redux version * Add embedded buttons * Handle embedded forms as new modals * Improve state handling * Remove unneeded changes * Fix lint * Migrate button_selector to typescript and make buttons disabled * Fix lint * Make errors work on Embedded and Modal forms * Handle button field type from app forms * Consider button fields also select fields and handle submit_buttons field * Make options not optional * Fix lint * Fix tests * Fix lint commit 2b766e23c10c7466dc1daad2373bc8185decbce6 Author: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Wed Nov 25 15:27:34 2020 -0500 Cloud Apps: Commands (#6912) * POC for mobile plugins * Add settings a post action locations and small improvements * Fix lint * Fix test and link plugin loading to websockets * Fetch integrations on websocket start * Change dispatch order, use constants, change plug variable name, and start fixing tests * Fix test * Use text instead of index to create the unique key * Fix lint * First approach to Apps implementation * Fixes and improvements * Fix lint * Address feedback and concile with the call PRs * client-side command parsing POC * typo * execute command option will now show when there are no subcommands * Update data model to have commands use AppletForm * remove unused code * tabs to spaces for types * WIP before redux repo merge * fix whitespace * Updates on command parser: - method renaming - using "label" again for binding labels, instead of "name" - command parser takes redux store in constructor, self-sufficient - positional arguments are now supported by index - change Applet to App - removed module-level state in parser file - app commands work on desktop and mobile web * update mm-redux * change call response to have form on top level * commands are working with multiple apps * types * fix lint * improvements * renaming functions * update mm-redux * lint * lint2 * mm-redux * lint * update tests * lint * update test * PR feedback * types Co-authored-by: Daniel Espino García commit b707de3f99d7da6715cef3f19265fea2b7466041 Author: Daniel Espino García Date: Thu Nov 19 11:40:23 2020 +0100 Some fixes to sync with lev-cleanup branch on apps (#7070) * Some fixes to sync with lev-clenaup branch on apps * Point to correct commit commit 24dfebc86efeaa9cd1f44889be188063cb545edc Author: Lev <1187448+levb@users.noreply.github.com> Date: Tue Nov 17 17:28:54 2020 -0800 Fixed to work with backend changes (#7017) Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> commit 8d65879eb3a2d4d11076872ebd7392705657947b Author: Daniel Espino García Date: Tue Nov 17 10:11:57 2020 +0100 Fix alignment when no icon is provided in post actions (#7071) commit 6a391fb3c26491ccee665c1c443d397322c9ca4e Author: Daniel Espino García Date: Tue Nov 3 20:53:06 2020 +0100 Locations (#6805) * POC for mobile plugins * Add settings a post action locations and small improvements * Fix lint * Fix test and link plugin loading to websockets * Fetch integrations on websocket start * Change dispatch order, use constants, change plug variable name, and start fixing tests * Fix test * Use text instead of index to create the unique key * Fix lint * First approach to Apps implementation * Fixes and improvements * Fix lint * Address feedback and concile with the call PRs * Update components/dot_menu/dot_menu.jsx * Handle open dialog call response * Properly use From * interactive dialogs can be used without trigger id * Refactor * Migrate global_actions to typescript * Migrate Dot menu to typescript * Migrate channel header plug to typescrip * Fix lint and some tests * Fix test * Bump redux dependency * Fix problem with channel header * Address feedback and update changes from redux Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> --- components/dot_menu/dot_menu.tsx | 7 +++--- components/dot_menu/index.ts | 6 +++-- .../app_command_parser/app_command_parser.ts | 6 +++-- .../app_command_parser_dependencies.ts | 3 +-- .../src/selectors/entities/apps.ts | 23 ++++++++++--------- .../channel_header_plug.tsx | 7 +++++- plugins/channel_header_plug/index.ts | 8 ++++--- utils/apps.ts | 13 +++++++---- 8 files changed, 45 insertions(+), 28 deletions(-) diff --git a/components/dot_menu/dot_menu.tsx b/components/dot_menu/dot_menu.tsx index 0198e97c2e73..f766e24eefd0 100644 --- a/components/dot_menu/dot_menu.tsx +++ b/components/dot_menu/dot_menu.tsx @@ -49,7 +49,7 @@ type Props = { enableEmojiPicker?: boolean; // TechDebt: Made non-mandatory while converting to typescript channelIsArchived?: boolean; // TechDebt: Made non-mandatory while converting to typescript currentTeamUrl?: string; // TechDebt: Made non-mandatory while converting to typescript - appBindings: AppBinding[]; + appBindings?: AppBinding[]; appsEnabled: boolean; /** @@ -113,12 +113,13 @@ type State = { } class DotMenu extends React.PureComponent { - static defaultProps = { + public static defaultProps: Partial = { commentCount: 0, isFlagged: false, isReadOnly: false, location: Locations.CENTER, pluginMenuItems: [], + appBindings: [], } private editDisableAction: DelayedAction; private buttonRef: React.RefObject; @@ -360,7 +361,7 @@ class DotMenu extends React.PureComponent { }) || []; let appBindings = [] as JSX.Element[]; - if (this.props.appsEnabled) { + if (this.props.appsEnabled && this.props.appBindings) { appBindings = this.props.appBindings.map((item) => { let icon: JSX.Element | undefined; if (item.icon) { diff --git a/components/dot_menu/index.ts b/components/dot_menu/index.ts index 5af0adf68e9b..e7f9a1da86d5 100644 --- a/components/dot_menu/index.ts +++ b/components/dot_menu/index.ts @@ -8,7 +8,7 @@ import {getLicense, getConfig} from 'mattermost-redux/selectors/entities/general import {getChannel} from 'mattermost-redux/selectors/entities/channels'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {getCurrentTeamId, getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; -import {getAppBindings} from 'mattermost-redux/selectors/entities/apps'; +import {makeAppBindingsSelector} from 'mattermost-redux/selectors/entities/apps'; import {AppBindingLocations} from 'mattermost-redux/constants/apps'; import {ActionFunc, ActionResult, GenericAction} from 'mattermost-redux/types/actions'; @@ -51,6 +51,8 @@ type Props = { enableEmojiPicker?: boolean; }; +const getPostMenuBindings = makeAppBindingsSelector(AppBindingLocations.POST_MENU_ITEM); + function mapStateToProps(state: GlobalState, ownProps: Props) { const {post} = ownProps; @@ -62,7 +64,7 @@ function mapStateToProps(state: GlobalState, ownProps: Props) { const currentTeamUrl = `${getSiteURL()}/${currentTeam.name}`; const apps = appsEnabled(state); - const appBindings = apps ? getAppBindings(state, AppBindingLocations.POST_MENU_ITEM) : []; + const appBindings = apps ? getPostMenuBindings(state) : undefined; return { channelIsArchived: isArchivedChannel(channel), diff --git a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts index 10081b1263af..bc52d08c3901 100644 --- a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts +++ b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts @@ -22,7 +22,7 @@ import { AppCallResponseTypes, AppCallTypes, AppFieldTypes, - getAppsBindings, + makeAppBindingsSelector, getChannel, getCurrentTeamId, doAppCall, @@ -72,6 +72,8 @@ interface Intl { formatMessage(config: {id: string; defaultMessage: string}, values?: {[name: string]: any}): string; } +const getCommandBindings = makeAppBindingsSelector(AppBindingLocations.COMMAND); + export class ParsedCommand { state: string = ParseState.Start; command: string; @@ -790,7 +792,7 @@ export class AppCommandParser { // getCommandBindings returns the commands in the redux store. // They are grouped by app id since each app has one base command getCommandBindings = (): AppBinding[] => { - const bindings = getAppsBindings(this.store.getState(), AppBindingLocations.COMMAND); + const bindings = getCommandBindings(this.store.getState()); return bindings; } diff --git a/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts b/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts index 3a6aae2c474c..f6e3550c889a 100644 --- a/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts +++ b/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts @@ -41,8 +41,7 @@ export { AppCallResponseTypes, } from 'mattermost-redux/constants/apps'; -import {getAppBindings as getAppsBindings} from 'mattermost-redux/selectors/entities/apps'; -export {getAppsBindings}; +export {makeAppBindingsSelector} from 'mattermost-redux/selectors/entities/apps'; export {getPost} from 'mattermost-redux/selectors/entities/posts'; export {getChannel, getCurrentChannel, getChannelByName as selectChannelByName} from 'mattermost-redux/selectors/entities/channels'; diff --git a/packages/mattermost-redux/src/selectors/entities/apps.ts b/packages/mattermost-redux/src/selectors/entities/apps.ts index 811d0e8ed674..8a12ed5370de 100644 --- a/packages/mattermost-redux/src/selectors/entities/apps.ts +++ b/packages/mattermost-redux/src/selectors/entities/apps.ts @@ -1,19 +1,20 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. + +import {createSelector} from 'reselect'; + import {GlobalState} from 'mattermost-redux/types/store'; import {AppBinding} from 'mattermost-redux/types/apps'; // This file's contents belong to the Apps Framework feature. // Apps Framework feature is experimental, and the contents of this file are // susceptible to breaking changes without pushing the major version of this package. -export function getAppBindings(state: GlobalState, location?: string): AppBinding[] { - if (!state.entities.apps.bindings) { - return []; - } - - if (location) { - const headerBindings = state.entities.apps.bindings.filter((b) => b.location === location); - return headerBindings.reduce((accum: AppBinding[], current: AppBinding) => accum.concat(current.bindings || []), []); - } - return state.entities.apps.bindings; -} +export const makeAppBindingsSelector = (location: string) => { + return createSelector( + (state: GlobalState) => state.entities.apps.bindings, + (bindings: AppBinding[]) => { + const headerBindings = bindings.filter((b) => b.location === location); + return headerBindings.reduce((accum: AppBinding[], current: AppBinding) => accum.concat(current.bindings || []), []); + }, + ); +}; diff --git a/plugins/channel_header_plug/channel_header_plug.tsx b/plugins/channel_header_plug/channel_header_plug.tsx index aa89cee90fa1..42de31d783b2 100644 --- a/plugins/channel_header_plug/channel_header_plug.tsx +++ b/plugins/channel_header_plug/channel_header_plug.tsx @@ -98,7 +98,7 @@ class CustomToggle extends React.PureComponent { type ChannelHeaderPlugProps = { intl: IntlShape; components: PluginComponent[]; - appBindings: AppBinding[]; + appBindings?: AppBinding[]; appsEnabled: boolean; channel: Channel; channelMember: ChannelMembership; @@ -113,6 +113,11 @@ type ChannelHeaderPlugState = { } class ChannelHeaderPlug extends React.PureComponent { + public static defaultProps: Partial = { + components: [], + appBindings: [], + } + constructor(props: ChannelHeaderPlugProps) { super(props); this.state = { diff --git a/plugins/channel_header_plug/index.ts b/plugins/channel_header_plug/index.ts index 1a865c8ada38..f07abaabfbc8 100644 --- a/plugins/channel_header_plug/index.ts +++ b/plugins/channel_header_plug/index.ts @@ -5,7 +5,7 @@ import {connect} from 'react-redux'; import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; -import {getAppBindings} from 'mattermost-redux/selectors/entities/apps'; +import {makeAppBindingsSelector} from 'mattermost-redux/selectors/entities/apps'; import {AppBindingLocations} from 'mattermost-redux/constants/apps'; import {ActionFunc, ActionResult, GenericAction} from 'mattermost-redux/types/actions'; @@ -18,11 +18,13 @@ import {appsEnabled} from 'utils/apps'; import ChannelHeaderPlug from './channel_header_plug'; +const getChannelHeaderBindings = makeAppBindingsSelector(AppBindingLocations.CHANNEL_HEADER_ICON); + function mapStateToProps(state: GlobalState) { const apps = appsEnabled(state); return { - components: state.plugins.components.ChannelHeaderButton || [], - appBindings: apps ? getAppBindings(state, AppBindingLocations.CHANNEL_HEADER_ICON) : [], + components: state.plugins.components.ChannelHeaderButton, + appBindings: apps ? getChannelHeaderBindings(state) : undefined, appsEnabled: apps, theme: getTheme(state), }; diff --git a/utils/apps.ts b/utils/apps.ts index ba4bc7ec2a13..cca799cd981d 100644 --- a/utils/apps.ts +++ b/utils/apps.ts @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {createSelector} from 'reselect'; + import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {AppCallResponseTypes} from 'mattermost-redux/constants/apps'; import {AppBinding, AppCall, AppCallRequest, AppCallValues, AppContext, AppExpand} from 'mattermost-redux/types/apps'; @@ -9,10 +11,13 @@ import {GlobalState} from 'mattermost-redux/types/store'; export const appsPluginID = 'com.mattermost.apps'; -export function appsEnabled(state: GlobalState): boolean { - const enabled = getConfig(state)?.['FeatureFlagAppsEnabled' as keyof Partial]; - return enabled === 'true'; -} +export const appsEnabled = createSelector( + (state: GlobalState) => getConfig(state), + (config?: Partial) => { + const enabled = config?.['FeatureFlagAppsEnabled' as keyof Partial]; + return enabled === 'true'; + }, +); export function fillBindingsInformation(binding?: AppBinding) { if (!binding) { From e4c354752af63eb00867336a0bd35e90dd8f6a30 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Wed, 31 Mar 2021 07:57:25 +0800 Subject: [PATCH 11/28] fix e2e for members with bots (#7763) --- .../integration/bot_accounts/in_lists_2_spec.js | 14 +++++--------- e2e/package.json | 1 - 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/e2e/cypress/integration/bot_accounts/in_lists_2_spec.js b/e2e/cypress/integration/bot_accounts/in_lists_2_spec.js index 3480ede9f190..f10edfce4906 100644 --- a/e2e/cypress/integration/bot_accounts/in_lists_2_spec.js +++ b/e2e/cypress/integration/bot_accounts/in_lists_2_spec.js @@ -9,8 +9,6 @@ // Group: @bot_accounts -import {zip, sortBy} from 'lodash'; - import {createBotPatch} from '../../support/api/bots'; import {generateRandomUser} from '../../support/api/user'; @@ -18,8 +16,6 @@ describe('Bots in lists', () => { let team; let channel; let testUser; - let bots; - let createdUsers; const STATUS_PRIORITY = { online: 0, @@ -43,14 +39,14 @@ describe('Bots in lists', () => { cy.makeClient().then(async (client) => { // # Create bots - bots = await Promise.all([ + const bots = await Promise.all([ client.createBot(createBotPatch()), client.createBot(createBotPatch()), client.createBot(createBotPatch()), ]); // # Create users - createdUsers = await Promise.all([ + const createdUsers = await Promise.all([ client.createUser(generateRandomUser()), client.createUser(generateRandomUser()), ]); @@ -80,15 +76,15 @@ describe('Bots in lists', () => { cy.get('#member-list-popover .more-modal__row .more-modal__name').then(async ($query) => { // # Extract usernames from jQuery collection - const usernames = $query.toArray().map(({innerText}) => innerText); + const usernames = $query.toArray().map(({innerText}) => innerText.split('\n')[0]); // # Get users const profiles = await client.getProfilesByUsernames(usernames); const statuses = await client.getStatusesByIds(profiles.map((user) => user.id)); - const users = zip(profiles, statuses).map(([profile, status]) => ({...profile, ...status})); + const users = Cypress._.zip(profiles, statuses).map(([profile, status]) => ({...profile, ...status})); // # Sort 'em - const sortedUsers = sortBy(users, [ + const sortedUsers = Cypress._.sortBy(users, [ ({is_bot: isBot}) => (isBot ? 1 : 0), // users first ({status}) => STATUS_PRIORITY[status], ({username}) => username, diff --git a/e2e/package.json b/e2e/package.json index 621327562b18..8c6671023515 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -24,7 +24,6 @@ "lodash.mapkeys": "4.6.0", "lodash.without": "4.4.0", "lodash.xor": "4.5.0", - "lodash": "4.17.21", "mattermost-redux": "5.33.0", "mime": "2.5.2", "mime-types": "2.1.29", From 5318afa703269a644f90d555d208959b0ff7bcb4 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Wed, 31 Mar 2021 08:49:12 -0300 Subject: [PATCH 12/28] make totalUsers optional (#7799) The `totalUsers` prop expected by `AddMembersButton` isn't actually required, since the component has code to handle when the prop is falsy. The property itself isn't available until after the background stats have loaded. --- .../channel_intro_message/add_members_button.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/post_view/channel_intro_message/add_members_button.tsx b/components/post_view/channel_intro_message/add_members_button.tsx index 4079d5b85af3..ab956182f488 100644 --- a/components/post_view/channel_intro_message/add_members_button.tsx +++ b/components/post_view/channel_intro_message/add_members_button.tsx @@ -25,7 +25,7 @@ import LoadingSpinner from 'components/widgets/loading/loading_spinner'; import MembersSvg from './members_illustration.svg'; export interface AddMembersButtonProps { - totalUsers: number; + totalUsers?: number; usersLimit: number; channel: Channel; setHeader: React.ReactNode; @@ -33,12 +33,13 @@ export interface AddMembersButtonProps { } const AddMembersButton: React.FC = ({totalUsers, usersLimit, channel, setHeader, theme}: AddMembersButtonProps) => { - const isPrivate = channel.type === Constants.PRIVATE_CHANNEL; - const inviteUsers = totalUsers < usersLimit; - if (!totalUsers) { return (); } + + const isPrivate = channel.type === Constants.PRIVATE_CHANNEL; + const inviteUsers = totalUsers < usersLimit; + return ( inviteUsers && !isPrivate ? lessThanMaxFreeUsers(setHeader, theme) : moreThanMaxFreeUsers(channel, setHeader) ); From c666df032a4445c98574a0e6b40e10a81af360b3 Mon Sep 17 00:00:00 2001 From: Crimson-riot <74468835+Crimson-riot@users.noreply.github.com> Date: Wed, 31 Mar 2021 10:09:06 -0500 Subject: [PATCH 13/28] [GH-16739, MM-18954] Converted channel info modal and associated tests to typescript (#7602) * converted channel info modal to typescript * lint fixes, updated tests to use testhelper, updated test snapshots * lint fixes for real this time * update mattermost-redux version in package.json * remove header and purpose from channel mock and update snapshot * fix import after mattermost-redux merged in Co-authored-by: Mattermod Co-authored-by: Harrison Healey Co-authored-by: Caleb Roseland --- ....snap => channel_info_modal.test.tsx.snap} | 16 ++++--- ...l.test.jsx => channel_info_modal.test.tsx} | 43 +++++++++++-------- ..._info_modal.jsx => channel_info_modal.tsx} | 41 ++++++++++++++---- components/channel_info_modal/index.js | 2 +- 4 files changed, 69 insertions(+), 33 deletions(-) rename components/channel_info_modal/__snapshots__/{channel_info_modal.test.jsx.snap => channel_info_modal.test.tsx.snap} (93%) rename components/channel_info_modal/{channel_info_modal.test.jsx => channel_info_modal.test.tsx} (63%) rename components/channel_info_modal/{channel_info_modal.jsx => channel_info_modal.tsx} (85%) diff --git a/components/channel_info_modal/__snapshots__/channel_info_modal.test.jsx.snap b/components/channel_info_modal/__snapshots__/channel_info_modal.test.tsx.snap similarity index 93% rename from components/channel_info_modal/__snapshots__/channel_info_modal.test.jsx.snap rename to components/channel_info_modal/__snapshots__/channel_info_modal.test.tsx.snap index 0c0fcc6a4662..a3b0e6f6075b 100644 --- a/components/channel_info_modal/__snapshots__/channel_info_modal.test.jsx.snap +++ b/components/channel_info_modal/__snapshots__/channel_info_modal.test.tsx.snap @@ -45,9 +45,10 @@ exports[`components/ChannelInfoModal should match snapshot 1`] = ` id="channel_info.about" /> - + name @@ -69,7 +70,7 @@ exports[`components/ChannelInfoModal should match snapshot 1`] = `
- http://localhost:8065/testteam/channels/testchannel + http://localhost:8065/DN/channels/DN
+ channel_id

@@ -131,9 +133,10 @@ exports[`components/ChannelInfoModal should match snapshot with channel props 1` id="channel_info.about" /> - + name @@ -203,7 +206,7 @@ exports[`components/ChannelInfoModal should match snapshot with channel props 1`
- http://localhost:8065/testteam/channels/testchannel + http://localhost:8065/DN/channels/DN
+ channel_id

diff --git a/components/channel_info_modal/channel_info_modal.test.jsx b/components/channel_info_modal/channel_info_modal.test.tsx similarity index 63% rename from components/channel_info_modal/channel_info_modal.test.jsx rename to components/channel_info_modal/channel_info_modal.test.tsx index f7dd015dc030..e3d44b2b59b5 100644 --- a/components/channel_info_modal/channel_info_modal.test.jsx +++ b/components/channel_info_modal/channel_info_modal.test.tsx @@ -5,16 +5,24 @@ import React from 'react'; import {Modal} from 'react-bootstrap'; import {shallow} from 'enzyme'; +import {Channel} from 'mattermost-redux/types/channels'; + import {mountWithIntl} from 'tests/helpers/intl-test-helper'; -import ChannelInfoModal from 'components/channel_info_modal/channel_info_modal.jsx'; +import ChannelInfoModal from 'components/channel_info_modal/channel_info_modal'; +import {TestHelper} from 'utils/test_helper'; describe('components/ChannelInfoModal', () => { + const mockChannel = TestHelper.getChannelMock({ + header: '', + purpose: '', + }); + const mockTeam = TestHelper.getTeamMock(); it('should match snapshot', () => { const wrapper = shallow( , ); @@ -23,9 +31,8 @@ describe('components/ChannelInfoModal', () => { }); it('should match snapshot with channel props', () => { - const channel = { - name: 'testchannel', - displayName: 'testchannel', + const channel: Channel = { + ...mockChannel, header: 'See ~test', purpose: 'And ~test too', props: { @@ -41,7 +48,7 @@ describe('components/ChannelInfoModal', () => { , ); @@ -54,22 +61,22 @@ describe('components/ChannelInfoModal', () => { const wrapper = mountWithIntl( , ); - wrapper.find(Modal).first().props().onExited(); + wrapper.find(Modal).first().props().onExited!(document.createElement('div')); expect(onHide).toHaveBeenCalled(); }); it('should call onHide when current channel changes', () => { const wrapper = mountWithIntl( , ); @@ -82,9 +89,9 @@ describe('components/ChannelInfoModal', () => { it('should call hide when RHS opens', () => { const wrapper = mountWithIntl( , ); diff --git a/components/channel_info_modal/channel_info_modal.jsx b/components/channel_info_modal/channel_info_modal.tsx similarity index 85% rename from components/channel_info_modal/channel_info_modal.jsx rename to components/channel_info_modal/channel_info_modal.tsx index 208cb4ddf7bc..6e26dd19d30c 100644 --- a/components/channel_info_modal/channel_info_modal.jsx +++ b/components/channel_info_modal/channel_info_modal.tsx @@ -7,19 +7,35 @@ import {Modal} from 'react-bootstrap'; import {FormattedMessage} from 'react-intl'; import {memoizeResult} from 'mattermost-redux/utils/helpers'; +import {Team} from 'mattermost-redux/types/teams'; +import {Channel} from 'mattermost-redux/types/channels'; import Markdown from 'components/markdown'; import GlobeIcon from 'components/widgets/icons/globe_icon'; import LockIcon from 'components/widgets/icons/lock_icon'; import ArchiveIcon from 'components/widgets/icons/archive_icon'; +import {ChannelNamesMap} from 'utils/text_formatting'; import Constants from 'utils/constants.jsx'; import {getSiteURL} from 'utils/url'; import * as Utils from 'utils/utils.jsx'; const headerMarkdownOptions = {singleline: false, mentionHighlight: false}; -export default class ChannelInfoModal extends React.PureComponent { +type Props = { + onHide: () => void; + channel: Channel; + currentChannel: Channel; + currentTeam: Team; + isRHSOpen?: boolean; + currentRelativeTeamUrl?: string; +}; + +type State = { + show: boolean; +}; + +export default class ChannelInfoModal extends React.PureComponent { static propTypes = { /** @@ -53,17 +69,13 @@ export default class ChannelInfoModal extends React.PureComponent { currentRelativeTeamUrl: PropTypes.string, }; - constructor(props) { + constructor(props: Props) { super(props); this.state = {show: true}; - - this.getHeaderMarkdownOptions = memoizeResult((channelNamesMap) => ( - {...headerMarkdownOptions, channelNamesMap} - )); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { const RHSChanged = !prevProps.isRHSOpen && this.props.isRHSOpen; const channelChanged = prevProps.channel?.id !== this.props.currentChannel?.id; if (RHSChanged || channelChanged) { @@ -75,7 +87,9 @@ export default class ChannelInfoModal extends React.PureComponent { this.setState({show: false}); } - handleFormattedTextClick = (e) => Utils.handleFormattedTextClick(e, this.props.currentRelativeTeamUrl); + handleFormattedTextClick = (e: React.MouseEvent) => Utils.handleFormattedTextClick(e, this.props.currentRelativeTeamUrl); + + getHeaderMarkdownOptions = memoizeResult((channelNamesMap: ChannelNamesMap) => ({...headerMarkdownOptions, channelNamesMap})); render() { let channel = this.props.channel; @@ -91,6 +105,17 @@ export default class ChannelInfoModal extends React.PureComponent { purpose: notFound, header: notFound, id: notFound, + team_id: notFound, + type: notFound, + delete_at: 0, + create_at: 0, + update_at: 0, + last_post_at: 0, + total_msg_count: 0, + extra_update_at: 0, + creator_id: notFound, + scheme_id: notFound, + group_constrained: false, }; } diff --git a/components/channel_info_modal/index.js b/components/channel_info_modal/index.js index 7c0d1581acf7..5f3ba415540d 100644 --- a/components/channel_info_modal/index.js +++ b/components/channel_info_modal/index.js @@ -8,7 +8,7 @@ import {getCurrentRelativeTeamUrl, getCurrentTeam} from 'mattermost-redux/select import {getIsRhsOpen} from 'selectors/rhs'; -import ChannelInfoModal from './channel_info_modal.jsx'; +import ChannelInfoModal from './channel_info_modal'; function mapStateToProps(state) { return { From 5a0556c0a0c24eaed9159415db5d3f940d527ce7 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Wed, 31 Mar 2021 14:15:42 -0300 Subject: [PATCH 14/28] MM-34371: handle undefined status in switcher (#7797) https://github.com/mattermost/mattermost-webapp/pull/6609 introduced the display of user statuses in the channel switcher, but without loading the status for users not already known to the client. This PR works around that limitation by correctly rendering an undefined status. It does not attempt to load the status for these users at this time. --- sass/components/_mentions.scss | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sass/components/_mentions.scss b/sass/components/_mentions.scss index 5d2333dcbcb4..0e7c6edacb5b 100644 --- a/sass/components/_mentions.scss +++ b/sass/components/_mentions.scss @@ -63,7 +63,6 @@ .status { width: auto; - margin: 0 8px; display: block; top: 1px; @@ -73,6 +72,12 @@ } } + // .mentions__name is way too multi-purpose. This rule targets its use in + // SwitchChannelSuggestion, applying margin in a way that handles null elements correctly. + .status, .mentions__fullname { + margin: 0 0 0 8px; + } + .fa-user { position: relative; top: -1px; From 619b9d131e0aa5e381045822e8d32ee3ced9348f Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Wed, 31 Mar 2021 14:52:21 -0300 Subject: [PATCH 15/28] make actorId optional (#7800) The combined system message component assembles a message from a set of system posts, but the TypeScript definition assumes that all system messages have actors. Some do not, including the opening system messages of `Town Square`, for which the join message is initiated by the server and not a user. --- .../combined_system_message/combined_system_message.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/post_view/combined_system_message/combined_system_message.tsx b/components/post_view/combined_system_message/combined_system_message.tsx index b89f786f29e4..dc0d686e09ad 100644 --- a/components/post_view/combined_system_message/combined_system_message.tsx +++ b/components/post_view/combined_system_message/combined_system_message.tsx @@ -174,7 +174,7 @@ export type Props = { currentUsername: string; intl: IntlShape; messageData: Array<{ - actorId: string; + actorId?: string; postType: string; userIds: string[]; }>; @@ -264,7 +264,7 @@ export class CombinedSystemMessage extends React.PureComponent { return usernames; } - renderFormattedMessage(postType: string, userIds: string[], actorId: string): JSX.Element { + renderFormattedMessage(postType: string, userIds: string[], actorId?: string): JSX.Element { const {formatMessage} = this.props.intl; const {currentUserId, currentUsername} = this.props; const usernames = this.getUsernamesByIds(userIds); @@ -320,7 +320,7 @@ export class CombinedSystemMessage extends React.PureComponent { ); } - renderMessage(postType: string, userIds: string[], actorId: string): JSX.Element { + renderMessage(postType: string, userIds: string[], actorId?: string): JSX.Element { return ( {this.renderFormattedMessage(postType, userIds, actorId)} From d54b3a1303562f049da14dcb03cb9962d0e959d1 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Thu, 1 Apr 2021 13:19:36 -0400 Subject: [PATCH 16/28] snapshot --- plugins/test/__snapshots__/main_menu_action.test.jsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/test/__snapshots__/main_menu_action.test.jsx.snap b/plugins/test/__snapshots__/main_menu_action.test.jsx.snap index 8d67244966ee..045e445df697 100644 --- a/plugins/test/__snapshots__/main_menu_action.test.jsx.snap +++ b/plugins/test/__snapshots__/main_menu_action.test.jsx.snap @@ -281,8 +281,8 @@ exports[`plugins/MainMenuActions should match snapshot and click plugin item for id="marketplaceModal" modalId="plugin_marketplace" show={true} - text="Marketplace" showUnread={true} + text="Marketplace" /> Date: Tue, 6 Apr 2021 00:42:01 -0400 Subject: [PATCH 17/28] pr feedback 1 --- actions/apps.ts | 2 +- actions/websocket_actions.jsx | 2 +- .../plugin_management/plugin_management.tsx | 4 +- components/apps_form/apps_form.tsx | 3 -- components/apps_form/apps_form_container.tsx | 3 -- .../apps_form_field/apps_form_field.tsx | 42 +++++++++---------- .../apps_form_select_field.tsx | 20 ++++----- components/apps_form/apps_form_header.tsx | 24 +++-------- components/apps_form/index.ts | 18 +------- 9 files changed, 42 insertions(+), 76 deletions(-) diff --git a/actions/apps.ts b/actions/apps.ts index bc52c1148b22..5dbe2a81d885 100644 --- a/actions/apps.ts +++ b/actions/apps.ts @@ -15,7 +15,7 @@ import {getSiteURL, shouldOpenInNewTab} from 'utils/url'; import {browserHistory} from 'utils/browser_history'; import {makeCallErrorResponse} from 'utils/apps'; -export function doAppCall(call: AppCallRequest, type: AppCallType, intl: any): ActionFunc { // eslint-disable-line @typescript-eslint/no-unused-vars +export function doAppCall(call: AppCallRequest, type: AppCallType, intl: any): ActionFunc { return async (dispatch: DispatchFunc) => { try { const res = await Client4.executeAppCall(call, type) as AppCallResponse; diff --git a/actions/websocket_actions.jsx b/actions/websocket_actions.jsx index 337c209d1fc4..d05cf7b37f38 100644 --- a/actions/websocket_actions.jsx +++ b/actions/websocket_actions.jsx @@ -183,6 +183,7 @@ export function reconnect(includeWebSocket = true) { const mostRecentId = getMostRecentPostIdInChannel(state, currentChannelId); const mostRecentPost = getPost(state, mostRecentId); dispatch(loadChannelsForCurrentUser()); + dispatch(handleRefreshAppsBindings()); if (mostRecentPost) { dispatch(syncPostsInChannel(currentChannelId, mostRecentPost.create_at)); } else { @@ -488,7 +489,6 @@ export function handleEvent(msg) { handleFirstAdminVisitMarketplaceStatusReceivedEvent(msg); break; - // Apps framework events case SocketEvents.APPS_FRAMEWORK_REFRESH_BINDINGS: { dispatch(handleRefreshAppsBindings(msg)); break; diff --git a/components/admin_console/plugin_management/plugin_management.tsx b/components/admin_console/plugin_management/plugin_management.tsx index 6e4d7c4d6148..908c1e16fc30 100644 --- a/components/admin_console/plugin_management/plugin_management.tsx +++ b/components/admin_console/plugin_management/plugin_management.tsx @@ -180,7 +180,7 @@ const PluginItem = ({ appsEnabled, isDisabled, }: PluginItemProps) => { - let activateButton: JSX.Element | null; + let activateButton: React.ReactNode; const activating = pluginStatus.state === PluginState.PLUGIN_STATE_STARTING; const deactivating = pluginStatus.state === PluginState.PLUGIN_STATE_STOPPING; @@ -257,7 +257,7 @@ const PluginItem = ({ /> ); } - let removeButton: JSX.Element | null = ( + let removeButton: React.ReactNode = ( {' - '} Promise; refreshOnSelect: (field: AppField, values: AppFormValues) => Promise<{data: AppCallResponse}>; }; - emojiMap: EmojiMap; } type Props = AppsFormProps & WrappedComponentProps<'intl'>; @@ -353,7 +351,6 @@ export class AppsForm extends React.PureComponent { )} {this.renderElements()} diff --git a/components/apps_form/apps_form_container.tsx b/components/apps_form/apps_form_container.tsx index d7e6eb006e8f..12c17d23df87 100644 --- a/components/apps_form/apps_form_container.tsx +++ b/components/apps_form/apps_form_container.tsx @@ -8,8 +8,6 @@ import {injectIntl, IntlShape} from 'react-intl'; import {AppCallResponse, AppField, AppForm, AppFormValues, AppSelectOption, AppCallType, AppCallRequest} from 'mattermost-redux/types/apps'; import {AppCallTypes, AppCallResponseTypes} from 'mattermost-redux/constants/apps'; -import EmojiMap from 'utils/emoji_map'; - import {makeCallErrorResponse} from 'utils/apps'; import {sendEphemeralPost} from 'actions/global_actions'; @@ -24,7 +22,6 @@ type Props = { actions: { doAppCall: (call: AppCallRequest, type: AppCallType, intl: IntlShape) => Promise<{data: AppCallResponse}>; }; - emojiMap: EmojiMap; }; type State = { diff --git a/components/apps_form/apps_form_field/apps_form_field.tsx b/components/apps_form/apps_form_field/apps_form_field.tsx index 6f69f797c2d8..431d6d412599 100644 --- a/components/apps_form/apps_form_field/apps_form_field.tsx +++ b/components/apps_form/apps_form_field/apps_form_field.tsx @@ -7,9 +7,9 @@ import {AppField, AppSelectOption} from 'mattermost-redux/types/apps'; import {Channel} from 'mattermost-redux/types/channels'; import {UserProfile} from 'mattermost-redux/types/users'; +import {AppFieldTypes} from 'mattermost-redux/constants/apps'; import {displayUsername} from 'mattermost-redux/utils/user_utils'; -import MenuActionProvider from 'components/suggestion/menu_action_provider'; import GenericUserProvider from 'components/suggestion/generic_user_provider.jsx'; import GenericChannelProvider from 'components/suggestion/generic_channel_provider.jsx'; @@ -42,16 +42,21 @@ export type Props = { } export default class AppsFormField extends React.PureComponent { - private provider?: Provider; + private providers: Provider[] = []; static defaultProps = { listComponent: ModalSuggestionList, }; + constructor(props: Props) { + super(props); + this.setProviders(); + } + handleSelected = (selected: AppSelectOption | UserProfile | Channel) => { const {name, field, onChange} = this.props; - if (field.type === 'user') { + if (field.type === AppFieldTypes.USER) { const user = selected as UserProfile; let selectedLabel = user.username; if (this.props.teammateNameDisplay) { @@ -59,7 +64,7 @@ export default class AppsFormField extends React.PureComponent { } const option = {label: selectedLabel, value: user.id}; onChange(name, option); - } else if (field.type === 'channel') { + } else if (field.type === AppFieldTypes.CHANNEL) { const channel = selected as Channel; const option = {label: channel.display_name, value: channel.id}; onChange(name, option); @@ -69,22 +74,17 @@ export default class AppsFormField extends React.PureComponent { } } - getProvider = (): Provider | void => { - if (this.provider) { - return this.provider; - } - + setProviders = () => { const {actions, field} = this.props; - if (field.type === 'user') { - this.provider = new GenericUserProvider(actions.autocompleteUsers); - } else if (field.type === 'channel') { - this.provider = new GenericChannelProvider(actions.autocompleteChannels); - } else if (field.options) { - const options = field.options.map(({label, value}) => ({text: label, value})); - this.provider = new MenuActionProvider(options); + + let providers: Provider[] = []; + if (field.type === AppFieldTypes.USER) { + providers = [new GenericUserProvider(actions.autocompleteUsers)]; + } else if (field.type === AppFieldTypes.CHANNEL) { + providers = [new GenericChannelProvider(actions.autocompleteChannels)]; } - return this.provider; + this.providers = providers; } render() { @@ -156,7 +156,7 @@ export default class AppsFormField extends React.PureComponent { resizable={false} /> ); - } else if (field.type === 'channel' || field.type === 'user') { + } else if (field.type === AppFieldTypes.CHANNEL || field.type === AppFieldTypes.USER) { let selectedValue: string | undefined; if (this.props.value) { selectedValue = (this.props.value as AppSelectOption).label; @@ -165,7 +165,7 @@ export default class AppsFormField extends React.PureComponent { { listComponent={listComponent} /> ); - } else if (field.type === 'static_select' || field.type === 'dynamic_select') { + } else if (field.type === AppFieldTypes.STATIC_SELECT || field.type === AppFieldTypes.DYNAMIC_SELECT) { return ( { value={this.props.value as AppSelectOption | null} /> ); - } else if (field.type === 'bool') { + } else if (field.type === AppFieldTypes.BOOL) { const boolValue = value as boolean; return ( @@ -79,8 +79,8 @@ export default class AppsFormSelectField extends React.PureComponent @@ -116,14 +116,12 @@ export default class AppsFormSelectField extends React.PureComponent {label} - {[ - - {selectComponent} -
- {helpText} -
-
, - ]} + + {selectComponent} +
+ {helpText} +
+
); } diff --git a/components/apps_form/apps_form_header.tsx b/components/apps_form/apps_form_header.tsx index 19394b2698d0..21e0b59a3266 100644 --- a/components/apps_form/apps_form_header.tsx +++ b/components/apps_form/apps_form_header.tsx @@ -3,32 +3,20 @@ import React from 'react'; -import * as Markdown from 'utils/markdown'; -import {getSiteURL} from 'utils/url'; -import EmojiMap from 'utils/emoji_map'; +import Markdown from 'components/markdown'; type Props = { id?: string; value: string; - emojiMap: EmojiMap; }; -const AppsFormHeader: React.FC = (props: Props) => { - const formattedMessage = Markdown.format( - props.value, - { - breaks: true, - sanitize: true, - gfm: true, - siteURL: getSiteURL(), - }, - props.emojiMap, - ); +const markdownOptions = {singleline: false, mentionHighlight: false}; +const AppsFormHeader: React.FC = (props: Props) => { return ( - ); }; diff --git a/components/apps_form/index.ts b/components/apps_form/index.ts index 280237f4e962..42aa67ff1c0e 100644 --- a/components/apps_form/index.ts +++ b/components/apps_form/index.ts @@ -4,12 +4,10 @@ import {connect} from 'react-redux'; import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux'; -import {GlobalState} from 'mattermost-redux/types/store'; import {ActionFunc, GenericAction} from 'mattermost-redux/types/actions'; -import {AppCallRequest, AppCallResponse, AppCallType, AppModalState} from 'mattermost-redux/types/apps'; +import {AppCallRequest, AppCallResponse, AppCallType} from 'mattermost-redux/types/apps'; import {doAppCall} from 'actions/apps'; -import {getEmojiMap} from 'selectors/emojis'; import AppsFormContainer from './apps_form_container'; @@ -17,18 +15,6 @@ type Actions = { doAppCall: (call: AppCallRequest, type: AppCallType) => Promise<{data: AppCallResponse}>; }; -function mapStateToProps(state: GlobalState, ownProps: {modal?: AppModalState}) { - const emojiMap = getEmojiMap(state); - if (!ownProps.modal) { - return {emojiMap}; - } - - return { - modal: ownProps.modal, - emojiMap, - }; -} - function mapDispatchToProps(dispatch: Dispatch) { return { actions: bindActionCreators, Actions>({ @@ -37,4 +23,4 @@ function mapDispatchToProps(dispatch: Dispatch) { }; } -export default connect(mapStateToProps, mapDispatchToProps)(AppsFormContainer); +export default connect(null, mapDispatchToProps)(AppsFormContainer); From 2d45986d4094bfab49846888c1431d741c341a08 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Tue, 6 Apr 2021 00:57:14 -0400 Subject: [PATCH 18/28] move appsEnabled to selectors file --- actions/command.ts | 2 +- actions/global_actions.tsx | 2 +- actions/marketplace.ts | 2 +- actions/websocket_actions.jsx | 2 +- components/admin_console/custom_plugin_settings/index.js | 3 ++- components/admin_console/plugin_management/index.ts | 2 +- components/dot_menu/index.ts | 2 +- .../post_view/post_body_additional_content/index.ts | 2 +- .../suggestion/command_provider/command_provider.tsx | 2 +- plugins/channel_header_plug/index.ts | 2 +- selectors/apps.ts | 8 ++++++++ utils/apps.ts | 8 -------- 12 files changed, 19 insertions(+), 18 deletions(-) create mode 100644 selectors/apps.ts diff --git a/actions/command.ts b/actions/command.ts index b545ca21b9f8..574608cd3a60 100644 --- a/actions/command.ts +++ b/actions/command.ts @@ -31,7 +31,7 @@ import {intlShim} from 'components/suggestion/command_provider/app_command_parse import {GlobalState} from 'types/store'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import {t} from 'utils/i18n'; diff --git a/actions/global_actions.tsx b/actions/global_actions.tsx index 23b72d52f3cc..3ee7e42fa2f2 100644 --- a/actions/global_actions.tsx +++ b/actions/global_actions.tsx @@ -43,7 +43,7 @@ import {filterAndSortTeamsByDisplayName} from 'utils/team_utils.jsx'; import * as Utils from 'utils/utils.jsx'; import SubMenuModal from '../components/widgets/menu/menu_modals/submenu_modal/submenu_modal'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import {openModal} from './views/modals'; diff --git a/actions/marketplace.ts b/actions/marketplace.ts index 33df42ebe571..498e6a7543a1 100644 --- a/actions/marketplace.ts +++ b/actions/marketplace.ts @@ -17,7 +17,7 @@ import {ActionTypes} from 'utils/constants'; import {isError} from 'types/actions'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import {executeCommand} from './command'; diff --git a/actions/websocket_actions.jsx b/actions/websocket_actions.jsx index d05cf7b37f38..decacc67408e 100644 --- a/actions/websocket_actions.jsx +++ b/actions/websocket_actions.jsx @@ -83,7 +83,7 @@ import {getSiteURL} from 'utils/url'; import {isGuest} from 'utils/utils'; import RemovedFromChannelModal from 'components/removed_from_channel_modal'; import InteractiveDialog from 'components/interactive_dialog'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; const dispatch = store.dispatch; const getState = store.getState; diff --git a/components/admin_console/custom_plugin_settings/index.js b/components/admin_console/custom_plugin_settings/index.js index 200576b1a480..b1645ecf5859 100644 --- a/components/admin_console/custom_plugin_settings/index.js +++ b/components/admin_console/custom_plugin_settings/index.js @@ -13,7 +13,8 @@ import {getAdminConsoleCustomComponents} from 'selectors/admin_console'; import SchemaAdminSettings from '../schema_admin_settings'; import {it} from '../admin_definition'; -import {appsEnabled, appsPluginID} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; +import {appsPluginID} from 'utils/apps'; import CustomPluginSettings from './custom_plugin_settings.jsx'; import getEnablePluginSetting from './enable_plugin_setting'; diff --git a/components/admin_console/plugin_management/index.ts b/components/admin_console/plugin_management/index.ts index 3ebca6776610..1846d4413f66 100644 --- a/components/admin_console/plugin_management/index.ts +++ b/components/admin_console/plugin_management/index.ts @@ -16,7 +16,7 @@ import { import {GenericAction} from 'mattermost-redux/types/actions'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import PluginManagement from './plugin_management'; diff --git a/components/dot_menu/index.ts b/components/dot_menu/index.ts index 5af0adf68e9b..806fc3d7a880 100644 --- a/components/dot_menu/index.ts +++ b/components/dot_menu/index.ts @@ -34,7 +34,7 @@ import * as PostUtils from 'utils/post_utils.jsx'; import {isArchivedChannel} from 'utils/channel_utils'; import {getSiteURL} from 'utils/url'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import DotMenu from './dot_menu'; diff --git a/components/post_view/post_body_additional_content/index.ts b/components/post_view/post_body_additional_content/index.ts index 57eefde650de..fe35f2684274 100644 --- a/components/post_view/post_body_additional_content/index.ts +++ b/components/post_view/post_body_additional_content/index.ts @@ -11,7 +11,7 @@ import {isEmbedVisible} from 'selectors/posts'; import {GlobalState} from 'types/store'; import {PostWillRenderEmbedPluginComponent} from 'types/store/plugins'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import PostBodyAdditionalContent, { Props, diff --git a/components/suggestion/command_provider/command_provider.tsx b/components/suggestion/command_provider/command_provider.tsx index b93e8fb5daab..56102f9a57e2 100644 --- a/components/suggestion/command_provider/command_provider.tsx +++ b/components/suggestion/command_provider/command_provider.tsx @@ -22,7 +22,7 @@ import {Constants} from 'utils/constants'; import Suggestion from '../suggestion'; import Provider from '../provider'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import {GlobalState} from 'types/store'; diff --git a/plugins/channel_header_plug/index.ts b/plugins/channel_header_plug/index.ts index 1a865c8ada38..60fa7c5573a8 100644 --- a/plugins/channel_header_plug/index.ts +++ b/plugins/channel_header_plug/index.ts @@ -14,7 +14,7 @@ import {AppCallRequest, AppCallType} from 'mattermost-redux/types/apps'; import {doAppCall} from 'actions/apps'; import {GlobalState} from 'types/store'; -import {appsEnabled} from 'utils/apps'; +import {appsEnabled} from 'selectors/apps'; import ChannelHeaderPlug from './channel_header_plug'; diff --git a/selectors/apps.ts b/selectors/apps.ts new file mode 100644 index 000000000000..b8899cefbf25 --- /dev/null +++ b/selectors/apps.ts @@ -0,0 +1,8 @@ +import {getConfig} from 'mattermost-redux/selectors/entities/general'; +import {ClientConfig} from 'mattermost-redux/types/config'; +import {GlobalState} from 'mattermost-redux/types/store'; + +export function appsEnabled(state: GlobalState): boolean { + const enabled = getConfig(state)?.['FeatureFlagAppsEnabled' as keyof Partial]; + return enabled === 'true'; +} diff --git a/utils/apps.ts b/utils/apps.ts index ba4bc7ec2a13..272d2997c6de 100644 --- a/utils/apps.ts +++ b/utils/apps.ts @@ -1,19 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {AppCallResponseTypes} from 'mattermost-redux/constants/apps'; import {AppBinding, AppCall, AppCallRequest, AppCallValues, AppContext, AppExpand} from 'mattermost-redux/types/apps'; -import {ClientConfig} from 'mattermost-redux/types/config'; -import {GlobalState} from 'mattermost-redux/types/store'; export const appsPluginID = 'com.mattermost.apps'; -export function appsEnabled(state: GlobalState): boolean { - const enabled = getConfig(state)?.['FeatureFlagAppsEnabled' as keyof Partial]; - return enabled === 'true'; -} - export function fillBindingsInformation(binding?: AppBinding) { if (!binding) { return; From d8209b4d43f7394fa5ff365bc0cbbe786dddfd32 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:05:00 -0400 Subject: [PATCH 19/28] tests and lint --- .../apps_form_container.test.jsx.snap | 6 --- .../apps_form_header.test.tsx.snap | 48 ++++++------------- components/apps_form/apps_form_container.tsx | 1 - .../apps_form/apps_form_header.test.tsx | 16 ++----- selectors/apps.ts | 3 ++ 5 files changed, 22 insertions(+), 52 deletions(-) diff --git a/components/apps_form/__snapshots__/apps_form_container.test.jsx.snap b/components/apps_form/__snapshots__/apps_form_container.test.jsx.snap index f64ba61530c3..aefd297b63a8 100644 --- a/components/apps_form/__snapshots__/apps_form_container.test.jsx.snap +++ b/components/apps_form/__snapshots__/apps_form_container.test.jsx.snap @@ -21,12 +21,6 @@ exports[`components/apps_form/AppsFormContainer should match snapshot 1`] = ` "path": "/form_url", } } - emojiMap={ - EmojiMap { - "customEmojis": Map {}, - "customEmojisArray": Array [], - } - } form={ Object { "fields": Array [ diff --git a/components/apps_form/__snapshots__/apps_form_header.test.tsx.snap b/components/apps_form/__snapshots__/apps_form_header.test.tsx.snap index 720dbb09b535..aed1c0d00308 100644 --- a/components/apps_form/__snapshots__/apps_form_header.test.tsx.snap +++ b/components/apps_form/__snapshots__/apps_form_header.test.tsx.snap @@ -1,45 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`components/apps_form/AppsFormHeader should not fail on empty value 1`] = ` - - - +/> `; exports[`components/apps_form/AppsFormHeader should render message with supported values 1`] = ` - - bold italic
link <br/> link target blank

", - } - } - id="testsupported" - /> - +/> `; diff --git a/components/apps_form/apps_form_container.tsx b/components/apps_form/apps_form_container.tsx index 12c17d23df87..43443e9033c0 100644 --- a/components/apps_form/apps_form_container.tsx +++ b/components/apps_form/apps_form_container.tsx @@ -199,7 +199,6 @@ class AppsFormContainer extends React.PureComponent { form={form} call={call} onHide={this.onHide} - emojiMap={this.props.emojiMap} actions={{ submit: this.submitForm, performLookupCall: this.performLookupCall, diff --git a/components/apps_form/apps_form_header.test.tsx b/components/apps_form/apps_form_header.test.tsx index 07d22fa8e25b..34af4f2afdbb 100644 --- a/components/apps_form/apps_form_header.test.tsx +++ b/components/apps_form/apps_form_header.test.tsx @@ -2,32 +2,26 @@ // See LICENSE.txt for license information. import React from 'react'; -import {mount} from 'enzyme'; - -import EmojiMap from 'utils/emoji_map'; +import {shallow} from 'enzyme'; import AppsFormHeader from './apps_form_header'; describe('components/apps_form/AppsFormHeader', () => { - const emojiMap = new EmojiMap(new Map()); - test('should render message with supported values', () => { - const descriptor = { + const props = { id: 'testsupported', value: '**bold** *italic* [link](https://mattermost.com/)
[link target blank](!https://mattermost.com/)', - emojiMap, }; - const wrapper = mount(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); test('should not fail on empty value', () => { - const descriptor = { + const props = { id: 'testblankvalue', value: '', - emojiMap, }; - const wrapper = mount(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/selectors/apps.ts b/selectors/apps.ts index b8899cefbf25..ff728560232e 100644 --- a/selectors/apps.ts +++ b/selectors/apps.ts @@ -1,3 +1,6 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {ClientConfig} from 'mattermost-redux/types/config'; import {GlobalState} from 'mattermost-redux/types/store'; From 7b5aec7063afa431f869a8bbcce8e2e16b9043fd Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:43:28 -0400 Subject: [PATCH 20/28] remove unnecessary undefined's due to selector changes --- components/dot_menu/index.ts | 2 +- plugins/channel_header_plug/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/dot_menu/index.ts b/components/dot_menu/index.ts index 49b00ad9aef9..1813fa2ab8bc 100644 --- a/components/dot_menu/index.ts +++ b/components/dot_menu/index.ts @@ -62,7 +62,7 @@ function mapStateToProps(state: GlobalState, ownProps: Props) { const currentTeamUrl = `${getSiteURL()}/${currentTeam.name}`; const apps = appsEnabled(state); - const appBindings = apps ? getPostMenuBindings(state) : undefined; + const appBindings = getPostMenuBindings(state); return { channelIsArchived: isArchivedChannel(channel), diff --git a/plugins/channel_header_plug/index.ts b/plugins/channel_header_plug/index.ts index 9631926367c4..28927a81040f 100644 --- a/plugins/channel_header_plug/index.ts +++ b/plugins/channel_header_plug/index.ts @@ -22,7 +22,7 @@ function mapStateToProps(state: GlobalState) { const apps = appsEnabled(state); return { components: state.plugins.components.ChannelHeaderButton, - appBindings: apps ? getChannelHeaderBindings(state) : undefined, + appBindings: getChannelHeaderBindings(state), appsEnabled: apps, theme: getTheme(state), }; From 6e07687f18b3489b85fee99adaa80bc3dd407e07 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Tue, 6 Apr 2021 02:30:25 -0400 Subject: [PATCH 21/28] fix app command parser test and add selector tests --- .../tests/app_command_parser_test_data.ts | 5 +- .../src/selectors/entities/apps.test.ts | 155 ++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 packages/mattermost-redux/src/selectors/entities/apps.test.ts diff --git a/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts b/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts index d372565f2e9a..6b977fb59ecb 100644 --- a/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts +++ b/components/suggestion/command_provider/app_command_parser/tests/app_command_parser_test_data.ts @@ -89,7 +89,10 @@ export const reduxTestState = { general: { license: {IsLicensed: 'false'}, serverVersion: '5.25.0', - config: {PostEditTimeLimit: -1}, + config: { + PostEditTimeLimit: -1, + FeatureFlagAppsEnabled: 'true', + }, }, }, }; diff --git a/packages/mattermost-redux/src/selectors/entities/apps.test.ts b/packages/mattermost-redux/src/selectors/entities/apps.test.ts new file mode 100644 index 000000000000..195578412fea --- /dev/null +++ b/packages/mattermost-redux/src/selectors/entities/apps.test.ts @@ -0,0 +1,155 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import * as Selectors from 'mattermost-redux/selectors/entities/apps'; +import {GlobalState} from 'types/store'; +import {AppBinding} from 'mattermost-redux/types/apps'; +import {AppBindingLocations} from 'mattermost-redux/constants/apps'; + +const makeNewState = (flag?: string, bindings?: AppBinding[]) => ({ + entities: { + general: { + config: { + FeatureFlagAppsEnabled: flag, + }, + }, + apps: { + bindings, + }, + }, +}) as unknown as GlobalState; + +describe('Selectors.Apps', () => { + describe('appsEnabled', () => { + it('should return true when feature flag is enabled', () => { + const state: GlobalState = makeNewState('true'); + const result = Selectors.appsEnabled(state); + expect(result).toEqual(true); + }); + + it('should return false when feature flag is disabled', () => { + let state: GlobalState = makeNewState('false'); + let result = Selectors.appsEnabled(state); + expect(result).toEqual(false); + + state = makeNewState(''); + result = Selectors.appsEnabled(state); + expect(result).toEqual(false); + + state = makeNewState(); + result = Selectors.appsEnabled(state); + expect(result).toEqual(false); + }); + }); + + describe('makeAppBindingsSelector', () => { + const allBindings = [ + { + location: '/post_menu', + bindings: [ + { + app_id: 'app1', + location: 'post-menu-1', + label: 'App 1 Post Menu', + }, + { + app_id: 'app2', + location: 'post-menu-2', + label: 'App 2 Post Menu', + }, + ], + }, + { + location: '/channel_header', + bindings: [ + { + app_id: 'app1', + location: 'channel-header-1', + label: 'App 1 Channel Header', + }, + { + app_id: 'app2', + location: 'channel-header-2', + label: 'App 2 Channel Header', + }, + ], + }, + { + location: '/command', + bindings: [ + { + app_id: 'app1', + location: 'command-1', + label: 'App 1 Command', + }, + { + app_id: 'app2', + location: 'command-2', + label: 'App 2 Command', + }, + ], + }, + ] as AppBinding[]; + + it('should return an empty array when feature flag is false', () => { + const state = makeNewState('false', allBindings); + const selector = Selectors.makeAppBindingsSelector(AppBindingLocations.POST_MENU_ITEM); + const result = selector(state); + expect(result).toEqual([]); + }); + + it('should return post menu bindings', () => { + const state = makeNewState('true', allBindings); + const selector = Selectors.makeAppBindingsSelector(AppBindingLocations.POST_MENU_ITEM); + const result = selector(state); + expect(result).toEqual([ + { + app_id: 'app1', + location: 'post-menu-1', + label: 'App 1 Post Menu', + }, + { + app_id: 'app2', + location: 'post-menu-2', + label: 'App 2 Post Menu', + }, + ]); + }); + + it('should return channel header bindings', () => { + const state = makeNewState('true', allBindings); + const selector = Selectors.makeAppBindingsSelector(AppBindingLocations.CHANNEL_HEADER_ICON); + const result = selector(state); + expect(result).toEqual([ + { + app_id: 'app1', + location: 'channel-header-1', + label: 'App 1 Channel Header', + }, + { + app_id: 'app2', + location: 'channel-header-2', + label: 'App 2 Channel Header', + }, + ]); + }); + + it('should return command bindings', () => { + const state = makeNewState('true', allBindings); + const selector = Selectors.makeAppBindingsSelector(AppBindingLocations.COMMAND); + const result = selector(state); + expect(result).toEqual([ + { + app_id: 'app1', + location: 'command-1', + label: 'App 1 Command', + }, + { + app_id: 'app2', + location: 'command-2', + label: 'App 2 Command', + }, + ]); + }); + }); +}); From 37fd46f5d9c959edd9c6f027a9c328048cd2d442 Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Tue, 6 Apr 2021 17:26:37 +0200 Subject: [PATCH 22/28] Memorize marketplace selectors (#7819) --- selectors/views/marketplace.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/selectors/views/marketplace.ts b/selectors/views/marketplace.ts index 48d8cc840bca..e7d870b83458 100644 --- a/selectors/views/marketplace.ts +++ b/selectors/views/marketplace.ts @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {createSelector} from 'reselect'; + import {isPlugin} from 'mattermost-redux/utils/marketplace'; import type {MarketplaceApp, MarketplacePlugin} from 'mattermost-redux/types/marketplace'; @@ -10,25 +12,28 @@ export const getPlugins = (state: GlobalState): MarketplacePlugin[] => state.vie export const getApps = (state: GlobalState): MarketplaceApp[] => state.views.marketplace.apps; -export const getListing = (state: GlobalState): Array => { - const plugins = getPlugins(state); - const apps = getApps(state); - - if (plugins) { - return (plugins as Array).concat(apps); - } +export const getListing = createSelector( + getPlugins, + getApps, + (plugins, apps) => { + if (plugins) { + return (plugins as Array).concat(apps); + } - return apps; -}; + return apps; + }, +); -export const getInstalledListing = (state: GlobalState): Array => - Object.values(getListing(state)).filter((i) => { +export const getInstalledListing = createSelector( + getListing, + (listing) => listing.filter((i) => { if (isPlugin(i)) { return i.installed_version !== ''; } return i.installed; - }); + }), +); export const getPlugin = (state: GlobalState, id: string): MarketplacePlugin | undefined => getPlugins(state).find(((p) => p.manifest && p.manifest.id === id)); From 390d7aaec028df92649058976f85d47ceca83fa8 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Tue, 6 Apr 2021 12:16:12 -0400 Subject: [PATCH 23/28] use useCallback hook --- components/button_selector.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/button_selector.tsx b/components/button_selector.tsx index 43c47a41e6d0..8b0fab95c705 100644 --- a/components/button_selector.tsx +++ b/components/button_selector.tsx @@ -26,11 +26,11 @@ const defaultProps: Partial = { }; const ButtonSelector: React.FC = (props: Props) => { - const onClick = (value: AppSelectOption) => { + const onClick = React.useCallback((value: AppSelectOption) => { if (props.onChange) { props.onChange(value); } - }; + }, [props.onChange]); const { footer, From c50b28ff6d3b940f2ef53af22f9f1fa9663582ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Tue, 6 Apr 2021 19:01:04 +0200 Subject: [PATCH 24/28] Lowercase pretext to ensure case indepenency (#7822) --- components/suggestion/emoticon_provider.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/suggestion/emoticon_provider.jsx b/components/suggestion/emoticon_provider.jsx index acb50ad80653..a88b30d19db0 100644 --- a/components/suggestion/emoticon_provider.jsx +++ b/components/suggestion/emoticon_provider.jsx @@ -60,7 +60,7 @@ export default class EmoticonProvider extends Provider { } handlePretextChanged(pretext, resultsCallback) { // Look for the potential emoticons at the start of the text, after whitespace, and at the start of emoji reaction commands - const captured = (/(^|\s|^\+|^-)(:([^:\s]*))$/g).exec(pretext); + const captured = (/(^|\s|^\+|^-)(:([^:\s]*))$/g).exec(pretext.toLowerCase()); if (!captured) { return false; } From 47b1cd5132b3c911a1872a542d5ba765fbea5a1e Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Wed, 7 Apr 2021 09:26:09 +0200 Subject: [PATCH 25/28] [MM-34491] Respect slash command root label (#7823) --- .../app_command_parser/app_command_parser.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts index bc52d08c3901..ea9a0e73765f 100644 --- a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts +++ b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts @@ -595,8 +595,9 @@ export class AppCommandParser { const result: AutocompleteSuggestion[] = []; const bindings = this.getCommandBindings(); + for (const binding of bindings) { - let base = binding.app_id; + let base = binding.label; if (!base) { continue; } @@ -604,10 +605,11 @@ export class AppCommandParser { if (base[0] !== '/') { base = '/' + base; } + if (base.startsWith(command)) { result.push({ + Complete: binding.label, Suggestion: base, - Complete: base.substring(1), Description: binding.description || '', Hint: binding.hint || '', IconData: binding.icon || '', @@ -811,7 +813,7 @@ export class AppCommandParser { isAppCommand = (pretext: string): boolean => { const command = pretext.toLowerCase(); for (const binding of this.getCommandBindings()) { - let base = binding.app_id; + let base = binding.label; if (!base) { continue; } From 1186f48d8faf2d24f2e12e2f9c01a811a0414b21 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Wed, 7 Apr 2021 13:50:39 -0400 Subject: [PATCH 26/28] use enum instead of keyMirror --- .../app_command_parser/app_command_parser.ts | 41 +++++++++---------- .../app_command_parser_dependencies.ts | 3 -- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts index ea9a0e73765f..d531bc7f83ab 100644 --- a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts +++ b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts @@ -30,7 +30,6 @@ import { EXECUTE_CURRENT_COMMAND_ITEM_ID, getExecuteSuggestion, displayError, - keyMirror, createCallRequest, selectUserByUsername, getUserByUsername, @@ -44,25 +43,25 @@ export type Store = { getState: () => GlobalState; } -export const ParseState = keyMirror({ - Start: null, - Command: null, - EndCommand: null, - CommandSeparator: null, - StartParameter: null, - ParameterSeparator: null, - Flag1: null, - Flag: null, - FlagValueSeparator: null, - StartValue: null, - NonspaceValue: null, - QuotedValue: null, - TickValue: null, - EndValue: null, - EndQuotedValue: null, - EndTickedValue: null, - Error: null, -}); +export enum ParseState { + Start = 'Start', + Command = 'Command', + EndCommand = 'EndCommand', + CommandSeparator = 'CommandSeparator', + StartParameter = 'StartParameter', + ParameterSeparator = 'ParameterSeparator', + Flag1 = 'Flag1', + Flag = 'Flag', + FlagValueSeparator = 'FlagValueSeparator', + StartValue = 'StartValue', + NonspaceValue = 'NonspaceValue', + QuotedValue = 'QuotedValue', + TickValue = 'TickValue', + EndValue = 'EndValue', + EndQuotedValue = 'EndQuotedValue', + EndTickedValue = 'EndTickedValue', + Error = 'Error', +} interface FormsCache { getForm: (location: string, binding: AppBinding) => Promise; @@ -75,7 +74,7 @@ interface Intl { const getCommandBindings = makeAppBindingsSelector(AppBindingLocations.COMMAND); export class ParsedCommand { - state: string = ParseState.Start; + state = ParseState.Start; command: string; i = 0; incomplete = ''; diff --git a/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts b/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts index f6e3550c889a..5d67dff97a1e 100644 --- a/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts +++ b/components/suggestion/command_provider/app_command_parser/app_command_parser_dependencies.ts @@ -51,9 +51,6 @@ export {getUserByUsername as selectUserByUsername} from 'mattermost-redux/select export {getUserByUsername} from 'mattermost-redux/actions/users'; export {getChannelByNameAndTeamName} from 'mattermost-redux/actions/channels'; -import keyMirror from 'mattermost-redux/utils/key_mirror'; -export {keyMirror}; - export {doAppCall} from 'actions/apps'; import {sendEphemeralPost} from 'actions/global_actions'; From 1fdc6b90a7db719f88d1c4231a01fa5cb762d890 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Wed, 7 Apr 2021 13:51:13 -0400 Subject: [PATCH 27/28] clarify isAppCommand usage, and note label public/private methods of app command parser --- .../app_command_parser/app_command_parser.ts | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts index d531bc7f83ab..cfd6ec0b8a13 100644 --- a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts +++ b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts @@ -95,13 +95,13 @@ export class ParsedCommand { this.intl = intl; } - asError = (message: string): ParsedCommand => { + private asError = (message: string): ParsedCommand => { this.state = ParseState.Error; this.error = message; return this; }; - errorMessage = (): string => { + public errorMessage = (): string => { return this.intl.formatMessage({ id: 'apps.error.parser', defaultMessage: 'Parsing error: {error}.\n```\n{command}\n{space}^\n```', @@ -113,7 +113,7 @@ export class ParsedCommand { } // matchBinding finds the closest matching command binding. - matchBinding = async (commandBindings: AppBinding[], autocompleteMode = false): Promise => { + public matchBinding = async (commandBindings: AppBinding[], autocompleteMode = false): Promise => { if (commandBindings.length === 0) { return this.asError(this.intl.formatMessage({ id: 'apps.error.parser.no_bindings', @@ -233,7 +233,7 @@ export class ParsedCommand { } // parseForm parses the rest of the command using the previously matched form. - parseForm = (autocompleteMode = false): ParsedCommand => { + public parseForm = (autocompleteMode = false): ParsedCommand => { if (this.state === ParseState.Error || !this.form) { return this; } @@ -664,7 +664,7 @@ export class AppCommandParser { } // composeCallFromParsed creates the form submission call - composeCallFromParsed = async (parsed: ParsedCommand): Promise => { + private composeCallFromParsed = async (parsed: ParsedCommand): Promise => { if (!parsed.binding) { return null; } @@ -685,7 +685,7 @@ export class AppCommandParser { return createCallRequest(call, context, {}, values, parsed.command); } - expandOptions = async (parsed: ParsedCommand, values: AppCallValues) => { + private expandOptions = async (parsed: ParsedCommand, values: AppCallValues) => { if (!parsed.form?.fields) { return true; } @@ -770,7 +770,7 @@ export class AppCommandParser { } // decorateSuggestionComplete applies the necessary modifications for a suggestion to be processed - decorateSuggestionComplete = (parsed: ParsedCommand, choice: AutocompleteSuggestion): AutocompleteSuggestion => { + private decorateSuggestionComplete = (parsed: ParsedCommand, choice: AutocompleteSuggestion): AutocompleteSuggestion => { if (choice.Complete && choice.Complete.endsWith(EXECUTE_CURRENT_COMMAND_ITEM_ID)) { return choice as AutocompleteSuggestion; } @@ -792,24 +792,26 @@ export class AppCommandParser { // getCommandBindings returns the commands in the redux store. // They are grouped by app id since each app has one base command - getCommandBindings = (): AppBinding[] => { + private getCommandBindings = (): AppBinding[] => { const bindings = getCommandBindings(this.store.getState()); return bindings; } // getChannel gets the channel in which the user is typing the command - getChannel = (): Channel | null => { + private getChannel = (): Channel | null => { const state = this.store.getState(); return getChannel(state, this.channelID); } - setChannelContext = (channelID: string, rootPostID?: string) => { + public setChannelContext = (channelID: string, rootPostID?: string) => { this.channelID = channelID; this.rootPostID = rootPostID; } - // isAppCommand determines if subcommand/form suggestions need to be returned - isAppCommand = (pretext: string): boolean => { + // isAppCommand determines if subcommand/form suggestions need to be returned. + // When this returns true, the caller knows that the parser should handle all suggestions for the current command string. + // When it returns false, the caller should call getSuggestionsBase() to check if there are any base commands that match the command string. + public isAppCommand = (pretext: string): boolean => { const command = pretext.toLowerCase(); for (const binding of this.getCommandBindings()) { let base = binding.label; @@ -829,7 +831,7 @@ export class AppCommandParser { } // getAppContext collects post/channel/team info for performing calls - getAppContext = (appID: string): AppContext => { + private getAppContext = (appID: string): AppContext => { const context: AppContext = { app_id: appID, location: AppBindingLocations.COMMAND, @@ -848,7 +850,7 @@ export class AppCommandParser { } // fetchForm unconditionaly retrieves the form for the given binding (subcommand) - fetchForm = async (binding: AppBinding): Promise => { + private fetchForm = async (binding: AppBinding): Promise => { if (!binding.call) { return undefined; } @@ -891,7 +893,7 @@ export class AppCommandParser { return callResponse.form; } - getForm = async (location: string, binding: AppBinding): Promise => { + public getForm = async (location: string, binding: AppBinding): Promise => { const form = this.forms[location]; if (form) { return form; @@ -905,7 +907,7 @@ export class AppCommandParser { } // displayError shows an error that was caught by the parser - displayError = (err: any): void => { + private displayError = (err: any): void => { let errStr = err as string; if (err.message) { errStr = err.message; @@ -914,7 +916,7 @@ export class AppCommandParser { } // getSuggestionsForSubCommands returns suggestions for a subcommand's name - getCommandSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { + private getCommandSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { if (!parsed.binding?.bindings?.length) { return []; } @@ -937,7 +939,7 @@ export class AppCommandParser { } // getParameterSuggestions computes suggestions for positional argument values, flag names, and flag argument values - getParameterSuggestions = async (parsed: ParsedCommand): Promise => { + private getParameterSuggestions = async (parsed: ParsedCommand): Promise => { switch (parsed.state) { case ParseState.StartParameter: { // see if there's a matching positional field @@ -967,7 +969,7 @@ export class AppCommandParser { } // getMissingFields collects the required fields that were not supplied in a submission - getMissingFields = (parsed: ParsedCommand): AppField[] => { + private getMissingFields = (parsed: ParsedCommand): AppField[] => { const form = parsed.form; if (!form) { return []; @@ -987,7 +989,7 @@ export class AppCommandParser { } // getFlagNameSuggestions returns suggestions for flag names - getFlagNameSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { + private getFlagNameSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { if (!parsed.form || !parsed.form.fields || !parsed.form.fields.length) { return []; } @@ -1024,7 +1026,7 @@ export class AppCommandParser { } // getSuggestionsForField gets suggestions for a positional or flag field value - getValueSuggestions = async (parsed: ParsedCommand, delimiter?: string): Promise => { + private getValueSuggestions = async (parsed: ParsedCommand, delimiter?: string): Promise => { if (!parsed || !parsed.field) { return []; } @@ -1058,7 +1060,7 @@ export class AppCommandParser { } // getStaticSelectSuggestions returns suggestions specified in the field's options property - getStaticSelectSuggestions = (parsed: ParsedCommand, delimiter?: string): AutocompleteSuggestion[] => { + private getStaticSelectSuggestions = (parsed: ParsedCommand, delimiter?: string): AutocompleteSuggestion[] => { const f = parsed.field as AutocompleteStaticSelect; const opts = f.options?.filter((opt) => opt.label.toLowerCase().startsWith(parsed.incomplete.toLowerCase())); @@ -1093,7 +1095,7 @@ export class AppCommandParser { } // getDynamicSelectSuggestions fetches and returns suggestions from the server - getDynamicSelectSuggestions = async (parsed: ParsedCommand, delimiter?: string): Promise => { + private getDynamicSelectSuggestions = async (parsed: ParsedCommand, delimiter?: string): Promise => { const f = parsed.field; if (!f) { // Should never happen @@ -1174,7 +1176,7 @@ export class AppCommandParser { }); } - makeSuggestionError = (message: string): AutocompleteSuggestion[] => { + private makeSuggestionError = (message: string): AutocompleteSuggestion[] => { const errMsg = this.intl.formatMessage({ id: 'apps.error', defaultMessage: 'Error: {error}', @@ -1191,7 +1193,7 @@ export class AppCommandParser { } // getUserSuggestions returns a suggestion with `@` if the user has not started typing - getUserSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { + private getUserSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { if (parsed.incomplete.trim().length === 0) { return [{ Complete: '', @@ -1206,7 +1208,7 @@ export class AppCommandParser { } // getChannelSuggestions returns a suggestion with `~` if the user has not started typing - getChannelSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { + private getChannelSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { if (parsed.incomplete.trim().length === 0) { return [{ Complete: '', @@ -1221,7 +1223,7 @@ export class AppCommandParser { } // getBooleanSuggestions returns true/false suggestions - getBooleanSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { + private getBooleanSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => { const suggestions: AutocompleteSuggestion[] = []; if ('true'.startsWith(parsed.incomplete)) { From 4180838c67d2daa90ccab006e4deaea55bc2459a Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Wed, 7 Apr 2021 13:51:46 -0400 Subject: [PATCH 28/28] use interface instead of type for Store --- .../command_provider/app_command_parser/app_command_parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts index cfd6ec0b8a13..e7c3e7b80141 100644 --- a/components/suggestion/command_provider/app_command_parser/app_command_parser.ts +++ b/components/suggestion/command_provider/app_command_parser/app_command_parser.ts @@ -38,7 +38,7 @@ import { selectChannelByName, } from './app_command_parser_dependencies'; -export type Store = { +export interface Store { dispatch: DispatchFunc; getState: () => GlobalState; } @@ -547,7 +547,7 @@ export class AppCommandParser { forms: {[location: string]: AppForm} = {}; constructor(store: Store|null, intl: Intl, channelID: string, rootPostID = '') { - this.store = store || getStore() as Store; + this.store = store || getStore(); this.channelID = channelID; this.rootPostID = rootPostID; this.intl = intl;