Skip to content

Commit 3a62507

Browse files
[Chat] Add a Narrator announcement when a message is deleted (#3844)
1 parent ccbb058 commit 3a62507

8 files changed

Lines changed: 78 additions & 6 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "fix",
4+
"workstream": "Accessibility",
5+
"comment": "Add a Narrator announcement when a message is deleted",
6+
"packageName": "@azure/communication-react",
7+
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
8+
"dependentChangeType": "patch"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "fix",
4+
"workstream": "Accessibility",
5+
"comment": "Add a Narrator announcement when a message is deleted",
6+
"packageName": "@azure/communication-react",
7+
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
8+
"dependentChangeType": "patch"
9+
}

packages/communication-react/review/beta/communication-react.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,6 +3187,7 @@ export interface MessageThreadStrings {
31873187
liveAuthorIntro: string;
31883188
messageContentAriaText: string;
31893189
messageContentMineAriaText: string;
3190+
messageDeletedAnnouncementAriaLabel: string;
31903191
messageReadCount?: string;
31913192
monday: string;
31923193
newMessagesIndicator: string;

packages/communication-react/review/stable/communication-react.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2461,6 +2461,7 @@ export interface MessageThreadStrings {
24612461
liveAuthorIntro: string;
24622462
messageContentAriaText: string;
24632463
messageContentMineAriaText: string;
2464+
messageDeletedAnnouncementAriaLabel: string;
24642465
messageReadCount?: string;
24652466
monday: string;
24662467
newMessagesIndicator: string;

packages/react-components/review/beta/react-components.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,7 @@ export interface MessageThreadStrings {
15331533
liveAuthorIntro: string;
15341534
messageContentAriaText: string;
15351535
messageContentMineAriaText: string;
1536+
messageDeletedAnnouncementAriaLabel: string;
15361537
messageReadCount?: string;
15371538
monday: string;
15381539
newMessagesIndicator: string;

packages/react-components/review/stable/react-components.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,7 @@ export interface MessageThreadStrings {
12201220
liveAuthorIntro: string;
12211221
messageContentAriaText: string;
12221222
messageContentMineAriaText: string;
1223+
messageDeletedAnnouncementAriaLabel: string;
12231224
messageReadCount?: string;
12241225
monday: string;
12251226
newMessagesIndicator: string;

packages/react-components/src/components/MessageThread.tsx

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
ChatMessageComponentWrapper,
5151
ChatMessageComponentWrapperProps
5252
} from './ChatMessage/ChatMessageComponentWrapper';
53+
import { Announcer } from './Announcer';
5354

5455
const isMessageSame = (first: ChatMessage, second: ChatMessage): boolean => {
5556
return (
@@ -211,6 +212,8 @@ export interface MessageThreadStrings {
211212
editBoxSubmitButton: string;
212213
/** String for action menu indicating there are more options */
213214
actionMenuMoreOptions?: string;
215+
/** Aria label to announce when a message is deleted */
216+
messageDeletedAnnouncementAriaLabel: string;
214217
/* @conditional-compile-remove(file-sharing) */
215218
/** String for download file button in file card */
216219
downloadFile: string;
@@ -715,6 +718,33 @@ export const MessageThreadWrapper = (props: MessageThreadProps): JSX.Element =>
715718
[onFetchAttachments]
716719
);
717720

721+
const localeStrings = useLocale().strings.messageThread;
722+
const strings = useMemo(() => ({ ...localeStrings, ...props.strings }), [localeStrings, props.strings]);
723+
724+
// id for the latest deleted message
725+
const [latestDeletedMessageId, setLatestDeletedMessageId] = useState<string | undefined>(undefined);
726+
// this value is used to check if a message is deleted for the previous value of messages array
727+
const previousMessagesRef = useRef<Message[]>([]);
728+
// an aria label for Narrator to notify when a message is deleted
729+
const [deletedMessageAriaLabel, setDeletedMessageAriaLabel] = useState<string | undefined>(undefined);
730+
731+
const onDeleteMessageCallback = useCallback(
732+
async (messageId: string): Promise<void> => {
733+
if (!onDeleteMessage) {
734+
return;
735+
}
736+
try {
737+
await onDeleteMessage(messageId);
738+
// reset deleted message label in case if there was a value already (messages are deleted 1 after another)
739+
setDeletedMessageAriaLabel(undefined);
740+
setLatestDeletedMessageId(messageId);
741+
} catch (e) {
742+
console.log('onDeleteMessage failed: messageId', messageId, 'error', e);
743+
}
744+
},
745+
[onDeleteMessage]
746+
);
747+
718748
const isAllChatMessagesLoadedRef = useRef(false);
719749
// isAllChatMessagesLoadedRef needs to be updated every time when a new adapter is set in order to display correct data
720750
// onLoadPreviousChatMessages is updated when a new adapter is set
@@ -741,6 +771,27 @@ export const MessageThreadWrapper = (props: MessageThreadProps): JSX.Element =>
741771
return newMessages;
742772
}, [newMessages]);
743773

774+
useEffect(() => {
775+
if (latestDeletedMessageId === undefined) {
776+
setDeletedMessageAriaLabel(undefined);
777+
} else {
778+
if (!previousMessagesRef.current.find((message) => message.messageId === latestDeletedMessageId)) {
779+
// the message is deleted in previousMessagesRef
780+
// no need to update deletedMessageAriaLabel
781+
setDeletedMessageAriaLabel(undefined);
782+
} else if (!messages.find((message) => message.messageId === latestDeletedMessageId)) {
783+
// the message is deleted in messages array but still exists in previousMessagesRef
784+
// update deletedMessageAriaLabel
785+
setDeletedMessageAriaLabel(strings.messageDeletedAnnouncementAriaLabel);
786+
} else {
787+
// the message exists in both arrays
788+
// no need to update deletedMessageAriaLabel
789+
setDeletedMessageAriaLabel(undefined);
790+
}
791+
}
792+
previousMessagesRef.current = messages;
793+
}, [latestDeletedMessageId, messages, strings.messageDeletedAnnouncementAriaLabel]);
794+
744795
const messagesRef = useRef(messages);
745796
const setMessagesRef = (messagesWithAttachedValue: Message[]): void => {
746797
messagesRef.current = messagesWithAttachedValue;
@@ -961,9 +1012,6 @@ export const MessageThreadWrapper = (props: MessageThreadProps): JSX.Element =>
9611012
[]
9621013
);
9631014

964-
const localeStrings = useLocale().strings.messageThread;
965-
const strings = useMemo(() => ({ ...localeStrings, ...props.strings }), [localeStrings, props.strings]);
966-
9671015
const defaultStatusRenderer = useCallback(
9681016
(
9691017
message: ChatMessage | /* @conditional-compile-remove(data-loss-prevention) */ BlockedMessage,
@@ -1006,7 +1054,7 @@ export const MessageThreadWrapper = (props: MessageThreadProps): JSX.Element =>
10061054
index,
10071055
onUpdateMessage,
10081056
onCancelEditMessage,
1009-
onDeleteMessage,
1057+
onDeleteMessageCallback,
10101058
onSendMessage,
10111059
props.disableEditing,
10121060
lastDeliveredChatMessage,
@@ -1021,7 +1069,7 @@ export const MessageThreadWrapper = (props: MessageThreadProps): JSX.Element =>
10211069
lastSendingChatMessage,
10221070
messages,
10231071
onCancelEditMessage,
1024-
onDeleteMessage,
1072+
onDeleteMessageCallback,
10251073
onSendMessage,
10261074
onUpdateMessage,
10271075
props.disableEditing,
@@ -1048,6 +1096,7 @@ export const MessageThreadWrapper = (props: MessageThreadProps): JSX.Element =>
10481096
)}
10491097
<LiveAnnouncer>
10501098
<FluentV9ThemeProvider v8Theme={theme}>
1099+
<Announcer announcementString={deletedMessageAriaLabel} ariaLive={'assertive'} />
10511100
<Chat
10521101
// styles?.chatContainer used in className and style prop as style prop can't handle CSS selectors
10531102
className={mergeClasses(classes.root, mergeStyles(styles?.chatContainer))}

packages/react-components/src/localization/locales/en-US/strings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@
153153
"downloadFile": "Download file",
154154
"blockedWarningText": "This message was deleted due to organizational policy.",
155155
"blockedWarningLinkText": "Details",
156-
"fileCardGroupMessage": "The message has {fileCount} attachment"
156+
"fileCardGroupMessage": "The message has {fileCount} attachment",
157+
"messageDeletedAnnouncementAriaLabel": "The message is deleted"
157158
},
158159
"errorBar": {
159160
"unableToReachChatService": "You are offline",

0 commit comments

Comments
 (0)