diff --git a/_locales/en/messages.json b/_locales/en/messages.json index bf1ef4dfeb..d50bb1a9f5 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -3236,6 +3236,10 @@ "messageformat": "Attachment missing from backup CDN", "description": "Shown for internal users for debugging backup attachments." }, + "icu:ToastManager__CannotAddMemberLabel": { + "messageformat": "Only admins can add member labels in this group.", + "description": "Shown when the user clicks on Member Label row in conversation details but they don't have permission to add a member label" + }, "icu:ToastManager__CannotEditMessage_24": { "messageformat": "Edits can only be applied within 24 hours from the time you sent this message.", "description": "Error message when you try to send an edit after message becomes too old" @@ -6156,6 +6160,10 @@ "messageformat": "Remove as admin", "description": "Button text for removing as admin button in Group Contact Details modal" }, + "icu:ContactModal--rm-admin--clear-label": { + "messageformat": "This will also clear their member label.", + "description": "Additional description shown in dialog when removing someone as an admin when they have a label" + }, "icu:ContactModal--make-admin": { "messageformat": "Make admin", "description": "Button text for make admin button in Group Contact Details modal" @@ -6408,6 +6416,10 @@ "messageformat": "Add members", "description": "The button that you can click to add new members" }, + "icu:ConversationDetailsMembershipList--add-member-label": { + "messageformat": "Add member label", + "description": "The button that you can click to add new members" + }, "icu:ConversationDetailsMembershipList--show-all": { "messageformat": "See all", "description": "This is a button on the conversation details to show all members" diff --git a/stylesheets/components/ContactName.scss b/stylesheets/components/ContactName.scss index 0bea1d592c..e412cf6c65 100644 --- a/stylesheets/components/ContactName.scss +++ b/stylesheets/components/ContactName.scss @@ -280,11 +280,15 @@ $contact-colors: ( .module-contact-name { &--label-pill { + @include mixins.font-body-small; + // Because of the font-size, it will be height=18px + border-radius: 9px; + font-weight: 400; display: inline-block; padding-inline: 6px; padding-block: 1px; - border-radius: 9px; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -301,6 +305,10 @@ $contact-colors: ( } &--label-pill--inner--contact-modal { + @include mixins.font-body-1; + // Because of font size, it will be height=22px + border-radius: 11px; + align-items: start; white-space: normal; } @@ -319,7 +327,7 @@ $contact-colors: ( } &--label-pill--quote { - margin-bottom: 2px; + margin-top: 1px; @include mixins.font-body-small; color: variables.$color-gray-90; diff --git a/stylesheets/components/ConversationDetails.scss b/stylesheets/components/ConversationDetails.scss index 2b6b54ef03..a2e98e59a6 100644 --- a/stylesheets/components/ConversationDetails.scss +++ b/stylesheets/components/ConversationDetails.scss @@ -45,10 +45,33 @@ } } - &-membership-list--member-label { + &-membership-list__member-label { @include mixins.font-body-small; } + &-membership-list__member-label-button { + @include mixins.font-body-small; + display: flex; + align-items: center; + color: light-dark( + rgba(variables.$color-black, 0.55), + rgba(variables.$color-white, 0.55) + ); + + &__chevron-icon { + height: 16px; + width: 16px; + margin-bottom: 1px; + margin-block-start: -2px; + + @include mixins.color-svg-themed( + '../images/icons/v3/chevron/chevron-right.svg', + rgba(variables.$color-black, 0.55), + rgba(variables.$color-white, 0.55) + ); + } + } + &__block-group { color: variables.$color-accent-red; } @@ -294,6 +317,10 @@ &::after { @include details-icon('../images/icons/v3/tag/tag.svg'); } + + &--disabled { + opacity: 0.5; + } } } } @@ -301,21 +328,18 @@ &-panel-row { $row-root-selector: '#{&}__root'; &__root { - align-items: center; border-radius: 5px; border: 2px solid transparent; - display: flex; padding-block: 8px; padding-inline: 24px; user-select: none; - width: 100%; @include mixins.font-body-2; &--button { color: inherit; background: none; - &:hover:not(:disabled) { + &[data-hovered]:not(:disabled) { background-color: light-dark( variables.$color-gray-02, variables.$color-gray-90 @@ -332,7 +356,7 @@ } @mixin keyboard-focus-state($color) { - &:focus { + &[data-focused='true'] { border-color: $color; } } @@ -345,6 +369,12 @@ } } + &__inner { + align-items: center; + display: flex; + width: 100%; + } + &__icon { margin-inline-end: 12px; flex-shrink: 0; @@ -376,6 +406,7 @@ } &__actions { + position: static; margin-inline-start: 12px; overflow: hidden; opacity: 0; @@ -451,6 +482,10 @@ user-select: text; } +.ConversationDetails__MemberLabel--disabled { + opacity: 0.5; +} + .ConversationDetails__CallHistoryGroup__List { list-style: none; margin: 0; diff --git a/stylesheets/components/Quote.scss b/stylesheets/components/Quote.scss index 4301e8a7da..b9c07dab93 100644 --- a/stylesheets/components/Quote.scss +++ b/stylesheets/components/Quote.scss @@ -210,10 +210,6 @@ } } -.module-quote__primary__author--with-label { - margin-top: -3px; -} - .module-quote__primary__text { @include mixins.font-body-1; diff --git a/ts/axo/AriaClickable.dom.tsx b/ts/axo/AriaClickable.dom.tsx index d932801c85..22c4b02e6f 100644 --- a/ts/axo/AriaClickable.dom.tsx +++ b/ts/axo/AriaClickable.dom.tsx @@ -116,6 +116,26 @@ export namespace AriaClickable { export const SubWidget: FC = memo(props => { return (
diff --git a/ts/components/ToastManager.dom.stories.tsx b/ts/components/ToastManager.dom.stories.tsx index 4618e671a4..8b4712ed92 100644 --- a/ts/components/ToastManager.dom.stories.tsx +++ b/ts/components/ToastManager.dom.stories.tsx @@ -56,6 +56,8 @@ function getToast(toastType: ToastType): AnyToast { }; case ToastType.CallQualitySurveySuccess: return { toastType: ToastType.CallQualitySurveySuccess }; + case ToastType.CannotAddMemberLabel: + return { toastType: ToastType.CannotAddMemberLabel }; case ToastType.CannotEditMessage: return { toastType: ToastType.CannotEditMessage }; case ToastType.CannotForwardEmptyMessage: diff --git a/ts/components/ToastManager.dom.tsx b/ts/components/ToastManager.dom.tsx index b270ce50f0..dbfee0a5d7 100644 --- a/ts/components/ToastManager.dom.tsx +++ b/ts/components/ToastManager.dom.tsx @@ -165,6 +165,14 @@ export function renderToast({ ); } + if (toastType === ToastType.CannotAddMemberLabel) { + return ( + + {i18n('icu:ToastManager__CannotAddMemberLabel')} + + ); + } + if (toastType === ToastType.CannotEditMessage) { return ( diff --git a/ts/components/conversation/ContactModal.dom.stories.tsx b/ts/components/conversation/ContactModal.dom.stories.tsx index abe636356d..e90a13a21b 100644 --- a/ts/components/conversation/ContactModal.dom.stories.tsx +++ b/ts/components/conversation/ContactModal.dom.stories.tsx @@ -12,6 +12,9 @@ import { HasStories } from '../../types/Stories.std.js'; import { ThemeType } from '../../types/Util.std.js'; import { getDefaultConversation } from '../../test-helpers/getDefaultConversation.std.js'; import { getFakeBadges } from '../../test-helpers/getFakeBadge.std.js'; +import { SignalService as Proto } from '../../protobuf/index.std.js'; + +const ACCESS_ENUM = Proto.AccessControl.AccessRequired; const { i18n } = window.SignalContext; @@ -123,6 +126,23 @@ AsAdmin.args = { areWeAdmin: true, }; +export const AsAdminViewingAdmin = Template.bind({}); +AsAdminViewingAdmin.args = { + areWeAdmin: true, + isAdmin: true, +}; + +export const AsAdminViewingAdminWithLabel = Template.bind({}); +AsAdminViewingAdminWithLabel.args = { + areWeAdmin: true, + isAdmin: true, + conversation: { + ...defaultGroup, + accessControlAttributes: ACCESS_ENUM.ADMINISTRATOR, + }, + contactLabelString: 'Contact Label', +}; + export const AsAdminWithNoGroupLink = Template.bind({}); AsAdminWithNoGroupLink.args = { areWeAdmin: true, diff --git a/ts/components/conversation/ContactModal.dom.tsx b/ts/components/conversation/ContactModal.dom.tsx index ec4f68cb29..ee2e320068 100644 --- a/ts/components/conversation/ContactModal.dom.tsx +++ b/ts/components/conversation/ContactModal.dom.tsx @@ -36,6 +36,9 @@ import type { ToggleGroupMemberLabelInfoModalType, } from '../../state/ducks/globalModals.preload.js'; import { GroupMemberLabel } from './ContactName.dom.js'; +import { SignalService as Proto } from '../../protobuf/index.std.js'; + +const ACCESS_ENUM = Proto.AccessControl.AccessRequired; const log = createLogger('ContactModal'); @@ -227,6 +230,35 @@ export function ContactModal({ break; } + if ( + isAdmin && + contactLabelString && + conversation.accessControlAttributes === ACCESS_ENUM.ADMINISTRATOR + ) { + modalNode = ( + toggleAdmin(conversation.id, contact.id), + text: isAdmin + ? i18n('icu:ContactModal--rm-admin') + : i18n('icu:ContactModal--make-admin'), + style: 'affirmative', + }, + ]} + i18n={i18n} + onClose={() => setSubModalState(SubModalState.None)} + title={i18n('icu:ContactModal--rm-admin-info', { + contact: contact.title, + })} + > + {i18n('icu:ContactModal--rm-admin--clear-label')} + + ); + break; + } + modalNode = ( {author} diff --git a/ts/components/conversation/conversation-details/ConversationDetails.dom.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetails.dom.stories.tsx index af473f3081..c750c85253 100644 --- a/ts/components/conversation/conversation-details/ConversationDetails.dom.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetails.dom.stories.tsx @@ -118,6 +118,7 @@ const createProps = ( replaceAvatar: action('replaceAvatar'), saveAvatarToDisk: action('saveAvatarToDisk'), setMuteExpiration: action('setMuteExpiration'), + showToast: action('showToast'), userAvatarData: [], toggleSafetyNumberModal: action('toggleSafetyNumberModal'), toggleAboutContactModal: action('toggleAboutContactModal'), @@ -163,6 +164,18 @@ export function Basic(): React.JSX.Element { return ; } +export function MemberLabelsEditDisabled(): React.JSX.Element { + const props = createProps(); + + return ; +} + +export function MemberLabelsCannotBeAdded(): React.JSX.Element { + const props = createProps(); + + return ; +} + export function SystemContact(): React.JSX.Element { const props = createProps(); const contact = getDefaultConversation(); diff --git a/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx b/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx index 8ca1c73a81..b4c71768ee 100644 --- a/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx @@ -3,6 +3,7 @@ import type { ReactNode } from 'react'; import React, { useEffect, useState, useCallback } from 'react'; +import classNames from 'classnames'; import { Button, ButtonIconType, ButtonVariant } from '../../Button.dom.js'; import type { @@ -65,6 +66,8 @@ import { } from '../InAnotherCallTooltip.dom.js'; import { BadgeSustainerInstructionsDialog } from '../../BadgeSustainerInstructionsDialog.dom.js'; import type { ContactModalStateType } from '../../../state/ducks/globalModals.preload.js'; +import type { ShowToastAction } from '../../../state/ducks/toast.preload.js'; +import { ToastType } from '../../../types/Toast.dom.js'; enum ModalState { AddingGroupMembers, @@ -102,6 +105,7 @@ export type StateProps = { pendingApprovalMemberships: ReadonlyArray; pendingAvatarDownload?: boolean; pendingMemberships: ReadonlyArray; + showToast: ShowToastAction; selectedNavTab: NavTab; startAvatarDownload: () => void; theme: ThemeType; @@ -208,6 +212,7 @@ export function ConversationDetails({ setMuteExpiration, showContactModal, showConversation, + showToast, startAvatarDownload, theme, toggleAboutContactModal, @@ -723,13 +728,20 @@ export function ConversationDetails({ )} {isGroup && ( { + pushPanelForConversation({ + type: PanelType.GroupMemberLabelEditor, + }); + }} startAddingNewMembers={() => { setModalState(ModalState.AddingGroupMembers); }} @@ -756,20 +768,36 @@ export function ConversationDetails({ right={hasGroupLink ? i18n('icu:on') : i18n('icu:off')} /> ) : null} - {canAddLabel && isEditMemberLabelEnabled ? ( + {isEditMemberLabelEnabled ? ( } - label={i18n('icu:ConversationDetails--member-label')} - onClick={() => + label={ +
+ {i18n('icu:ConversationDetails--member-label')} +
+ } + onClick={() => { + if (!canAddLabel) { + showToast({ toastType: ToastType.CannotAddMemberLabel }); + return; + } + pushPanelForConversation({ type: PanelType.GroupMemberLabelEditor, - }) - } + }); + }} /> ) : null} ; memberColors: Map; showContactModal: (contactId: string, conversationId?: string) => void; + showLabelEditor: () => void; startAddingNewMembers?: () => void; theme: ThemeType; }; @@ -79,13 +83,16 @@ function sortMemberships( export function ConversationDetailsMembershipList({ canAddNewMembers, + canAddLabel, conversationId, getPreferredBadge, i18n, + isEditMemberLabelEnabled, maxShownMemberCount = 5, memberColors, memberships, showContactModal, + showLabelEditor, startAddingNewMembers, theme, }: Props): React.JSX.Element { @@ -141,7 +148,7 @@ export function ConversationDetailsMembershipList({ />
{labelString && contactNameColor && ( -
+
)} + {canAddLabel && + isEditMemberLabelEnabled && + member.isMe && + (!labelString || !contactNameColor) && ( + + + + )}
} right={isAdmin ? i18n('icu:GroupV2--admin') : ''} diff --git a/ts/components/conversation/conversation-details/GroupLinkManagement.dom.tsx b/ts/components/conversation/conversation-details/GroupLinkManagement.dom.tsx index f67632a0ae..f9e9b175f9 100644 --- a/ts/components/conversation/conversation-details/GroupLinkManagement.dom.tsx +++ b/ts/components/conversation/conversation-details/GroupLinkManagement.dom.tsx @@ -149,7 +149,6 @@ export function GroupLinkManagement({ /> } label={i18n('icu:GroupLinkManagement--share')} - ref={!isAdmin ? focusRef : undefined} onClick={() => { if (conversation.groupLink) { drop(copyGroupLink(conversation.groupLink)); diff --git a/ts/components/conversation/conversation-details/GroupMemberLabelEditor.dom.tsx b/ts/components/conversation/conversation-details/GroupMemberLabelEditor.dom.tsx index f98996df31..5bb68a7da6 100644 --- a/ts/components/conversation/conversation-details/GroupMemberLabelEditor.dom.tsx +++ b/ts/components/conversation/conversation-details/GroupMemberLabelEditor.dom.tsx @@ -101,7 +101,7 @@ export function GroupMemberLabelEditor({ const isDirty = labelEmoji !== existingLabelEmoji || labelString !== existingLabelString; - const canSave = Boolean(isDirty && labelString); + const canSave = isDirty; const spinner = isSaving ? { 'aria-label': i18n('icu:ConversationDetails--member-label--saving'), diff --git a/ts/components/conversation/conversation-details/PanelRow.dom.tsx b/ts/components/conversation/conversation-details/PanelRow.dom.tsx index f47d100fa0..9ec57d1876 100644 --- a/ts/components/conversation/conversation-details/PanelRow.dom.tsx +++ b/ts/components/conversation/conversation-details/PanelRow.dom.tsx @@ -1,9 +1,10 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React from 'react'; +import React, { useId } from 'react'; import classNames from 'classnames'; import { bemGenerator } from './util.std.js'; +import { AriaClickable } from '../../../axo/AriaClickable.dom.js'; export type Props = { alwaysShowActions?: boolean; @@ -19,55 +20,61 @@ export type Props = { const bem = bemGenerator('ConversationDetails-panel-row'); -export const PanelRow = React.forwardRef( - function PanelRowInner( - { - alwaysShowActions, - className, - disabled, - icon, - label, - info, - right, - actions, - onClick, - }: Props, - ref: React.Ref - ) { - const content = ( - <> - {icon !== undefined ?
{icon}
: null} -
-
{label}
- {info !== undefined ? ( -
{info}
- ) : null} -
- {right !== undefined ? ( -
{right}
- ) : null} - {actions !== undefined ? ( -
- {actions} -
- ) : null} - - ); +export function PanelRow({ + alwaysShowActions, + className, + disabled, + icon, + label, + info, + right, + actions, + onClick, +}: Props): React.ReactNode { + const labelId = useId(); + const subWidget = + actions !== undefined ? ( +
{actions}
+ ) : null; - if (onClick) { - return ( - - ); - } - - return
{content}
; + {subWidget && ( + {subWidget} + )} + + + ); } -); + + return ( +
+
+ {content} + {subWidget} +
+
+ ); +} diff --git a/ts/groups.preload.ts b/ts/groups.preload.ts index be9a1d4cdb..2b3971399d 100644 --- a/ts/groups.preload.ts +++ b/ts/groups.preload.ts @@ -1271,6 +1271,25 @@ export function buildModifyMemberRoleChange({ actions.version = (group.revision || 0) + 1; actions.modifyMemberRoles = [toggleAdmin]; + const membership = group.membersV2?.find(member => member.aci === serviceId); + const onlyAdminsCanChangeAttributes = + group.accessControl?.attributes === + Proto.AccessControl.AccessRequired.ADMINISTRATOR; + const wasPreviouslyAnAdmin = + membership?.role === Proto.Member.Role.ADMINISTRATOR; + const nowNotAnAdmin = role !== Proto.Member.Role.ADMINISTRATOR; + + if ( + membership?.labelString && + onlyAdminsCanChangeAttributes && + wasPreviouslyAnAdmin && + nowNotAnAdmin + ) { + const modifyLabel = new Proto.GroupChange.Actions.ModifyMemberLabelAction(); + modifyLabel.userId = userIdCipherText; + actions.modifyMemberLabels = [modifyLabel]; + } + return actions; } diff --git a/ts/state/smart/ConversationDetails.preload.tsx b/ts/state/smart/ConversationDetails.preload.tsx index 53a40b9ed6..776cd661dd 100644 --- a/ts/state/smart/ConversationDetails.preload.tsx +++ b/ts/state/smart/ConversationDetails.preload.tsx @@ -52,6 +52,7 @@ import { drop } from '../../util/drop.std.js'; import { DataReader } from '../../sql/Client.preload.js'; import { isFeaturedEnabledSelector } from '../../util/isFeatureEnabled.dom.js'; import { getCanAddLabel } from '../../types/GroupMemberLabels.std.js'; +import { useToastActions } from '../ducks/toast.preload.js'; const { sortBy } = lodash; @@ -149,6 +150,7 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({ toggleEditNicknameAndNoteModal, toggleSafetyNumberModal, } = useGlobalModalActions(); + const { showToast } = useToastActions(); const conversation = conversationSelector(conversationId); assertDev( @@ -273,6 +275,7 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({ setMuteExpiration={setMuteExpiration} showContactModal={showContactModal} showConversation={showConversation} + showToast={showToast} startAvatarDownload={() => startAvatarDownload(conversationId)} theme={theme} toggleAboutContactModal={toggleAboutContactModal} diff --git a/ts/state/smart/GV1Members.preload.tsx b/ts/state/smart/GV1Members.preload.tsx index a4c7fbc332..ae4b72c952 100644 --- a/ts/state/smart/GV1Members.preload.tsx +++ b/ts/state/smart/GV1Members.preload.tsx @@ -3,6 +3,7 @@ import React, { memo } from 'react'; import { useSelector } from 'react-redux'; +import { noop } from 'lodash'; import { ConversationDetailsMembershipList } from '../../components/conversation/conversation-details/ConversationDetailsMembershipList.dom.js'; import { assertDev } from '../../util/assert.std.js'; @@ -50,14 +51,17 @@ export const SmartGV1Members = memo(function SmartGV1Members({ return ( ); diff --git a/ts/test-mock/backups/backups_test.node.ts b/ts/test-mock/backups/backups_test.node.ts index 2e9fb9cd77..0be0b2e6b7 100644 --- a/ts/test-mock/backups/backups_test.node.ts +++ b/ts/test-mock/backups/backups_test.node.ts @@ -179,7 +179,7 @@ describe('backups', function (this: Mocha.Suite) { await window.getByRole('menuitem', { name: 'Chat settings' }).click(); await conversationStack - .locator('.ConversationDetails__chat-color') + .getByRole('button', { name: 'Chat color' }) .click(); await conversationStack .locator('.ChatColorPicker__bubble--infrared') diff --git a/ts/test-mock/pnp/accept_gv2_invite_test.node.ts b/ts/test-mock/pnp/accept_gv2_invite_test.node.ts index 769c97091c..e6801d0efc 100644 --- a/ts/test-mock/pnp/accept_gv2_invite_test.node.ts +++ b/ts/test-mock/pnp/accept_gv2_invite_test.node.ts @@ -161,7 +161,8 @@ describe('pnp/accept gv2 invite', function (this: Mocha.Suite) { debug('Leave the group through settings'); await conversationStack - .locator('.conversation-details-panel >> "Leave group"') + .locator('.conversation-details-panel') + .getByRole('button', { name: 'Leave group' }) .click(); await window @@ -277,10 +278,7 @@ describe('pnp/accept gv2 invite', function (this: Mocha.Suite) { .locator('.module-ConversationHeader__header__info__title') .click(); await conversationStack - .locator( - '.ConversationDetails-panel-row__root--button >> ' + - 'text=Requests & Invites' - ) + .getByRole('button', { name: 'Requests & Invites' }) .click(); await conversationStack .locator('.ConversationDetails__tabs__tab >> text=Invites (1)') diff --git a/ts/test-mock/pnp/username_test.node.ts b/ts/test-mock/pnp/username_test.node.ts index 90aff37d96..d45113dd02 100644 --- a/ts/test-mock/pnp/username_test.node.ts +++ b/ts/test-mock/pnp/username_test.node.ts @@ -195,7 +195,7 @@ describe('pnp/username', function (this: Mocha.Suite) { debug('opening username editor'); const profileEditor = window.locator('.ProfileEditor'); - await profileEditor.locator('.ProfileEditor__row >> "Username"').click(); + await profileEditor.getByRole('button', { name: 'Username' }).click(); debug('entering new username'); const usernameField = profileEditor.locator('.Input__input'); @@ -218,9 +218,7 @@ describe('pnp/username', function (this: Mocha.Suite) { debug('checking the username is saved'); { - await profileEditor - .locator(`.ProfileEditor__row >> "${username}"`) - .waitFor(); + await profileEditor.getByRole('button', { name: username }).waitFor(); const uuid = await server.lookupByUsername(username); assert.strictEqual(uuid, phone.device.aci); @@ -269,7 +267,7 @@ describe('pnp/username', function (this: Mocha.Suite) { await window .locator('.module-Modal .module-Modal__button-footer button >> "Delete"') .click(); - await profileEditor.locator('.ProfileEditor__row >> "Username"').waitFor(); + await profileEditor.getByRole('button', { name: 'Username' }).waitFor(); debug('confirming username deletion'); { diff --git a/ts/test-mock/storage/call_links_test.node.ts b/ts/test-mock/storage/call_links_test.node.ts index d18817bdbf..7c8883ef46 100644 --- a/ts/test-mock/storage/call_links_test.node.ts +++ b/ts/test-mock/storage/call_links_test.node.ts @@ -114,7 +114,9 @@ describe('storage service', function (this: Mocha.Suite) { '.CallsTab__ConversationCallDetails' ); await callLinkDetails.waitFor(); - const deleteButton = await window.getByText('Delete link'); + const deleteButton = await window.getByRole('button', { + name: 'Delete link', + }); await deleteButton.click(); const confirmModal = await window.getByTestId( 'ConfirmationDialog.CallLinkDetails__DeleteLinkModal' diff --git a/ts/test-mock/storage/conflict_test.node.ts b/ts/test-mock/storage/conflict_test.node.ts index 8ccfe9a476..245daf889d 100644 --- a/ts/test-mock/storage/conflict_test.node.ts +++ b/ts/test-mock/storage/conflict_test.node.ts @@ -292,7 +292,7 @@ describe('storage service', function (this: Mocha.Suite) { await window.getByText('Fun link').click(); await window .locator('.CallsTab__ConversationCallDetails') - .getByText('Delete link') + .getByRole('button', { name: 'Delete link' }) .click(); const confirmModal = await window.getByTestId( diff --git a/ts/types/Toast.dom.tsx b/ts/types/Toast.dom.tsx index afe215a69b..d4b962dc33 100644 --- a/ts/types/Toast.dom.tsx +++ b/ts/types/Toast.dom.tsx @@ -15,6 +15,7 @@ export enum ToastType { CallQualitySurveySuccess = 'CallQualitySurveySuccess', CaptchaFailed = 'CaptchaFailed', CaptchaSolved = 'CaptchaSolved', + CannotAddMemberLabel = 'CannotAddMemberLabel', CannotEditMessage = 'CannotEditMessage', CannotForwardEmptyMessage = 'CannotForwardEmptyMessage', CannotMixMultiAndNonMultiAttachments = 'CannotMixMultiAndNonMultiAttachments', @@ -124,6 +125,7 @@ export type AnyToast = parameters: { canRetry: boolean }; } | { toastType: ToastType.CallQualitySurveySuccess } + | { toastType: ToastType.CannotAddMemberLabel } | { toastType: ToastType.CannotEditMessage } | { toastType: ToastType.CannotForwardEmptyMessage } | { toastType: ToastType.CannotMixMultiAndNonMultiAttachments }