Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "send failed retry" prompt to live region #4362

Merged
merged 52 commits into from
Aug 3, 2022

Conversation

compulim
Copy link
Contributor

@compulim compulim commented Jul 22, 2022

Fixes #4211. Fixes #4322.

Changelog Entry

Breaking changes

  • The activity.channelData.state property is being deprecated in favor of the updated activity.channelData['webchat:send-status'] property, main differences:
    • The new 'webchat:send-status' property will become "send failed" when the chat adapter failed to send the activity or after passing a hardcoded 5 minute timeout
    • Previously the state property would become "send failed" when the chat adapter failed to send the activity or after passing a timeout as defined in styleOptions.sendTimeout
    • See PR #4362 for details

Changed

  • Resolves #4322. Improved error messages for sending activities, by @compulim in PR #4362
  • Resolves #4211. Added new useSendStatusByActivityKey hook to check the UI send status of an outgoing activity, by @compulim in PR #4362
    • The send status returned by this hook is designed to display different UIs that reflect the "sending", "send failed" or "sent" status of the activity
    • When modifying styleOptions.sendTimeout prop, the send status returned by this hook may transition from "send failed" to "sending", and vice versa
    • This is different from the send status provided by the chat adapter, namely activity.channelData['webchat:send-status']

Fixed

  • Fixes #4211. Screen reader should read when an activity was failed to send, by @compulim, in PR #4326, also fixed:
    • The "send failed" status on the activity should show up as soon as the chat adapter failed to send the activity

Description

When an activity was failed to send (e.g. send timeout, network error, etc.), a "Send failed, retry" prompt will be shown on the chat history. However, this retry prompt is not narrated through live region.

This pull request is to add the retry prompt to the live region to make sure screen reader users are aware that the message they sent is failing and they can retry.

The "send failed" prompt will only read for failing message that are also non-presentational (e.g. not event or typing activities).

While we are working on this PR, we discovered a few flaws in the current version of Web Chat. The flaws are documented in the design section of this PR description:

  • channelData.state === 'send failed' was intentionally ignored
    • Activities that are legitimately failed (not timed out) will not be reflected until timeout has passed
    • In other words, in airplane mode (no network), outgoing activities should show up as failed immediately
    • However, currently, it take 20s for an activity to show up as failed
  • Outgoing activity may not have channelData.state set momentarily
    • While sending an activity, if the chat service echoed back but the postActivity() call did not return yet, the activity-in-transit should still have send status of sending, instead of unset
  • createDirectLineWithTranscript does not work properly for outgoing activities because the postActivitySaga is not triggered
    • Some of our existing tests forcefully put channelData.state in the createDirectLineWithTranscript.activity$ to emulate the effect of postActivitySaga, this behavior is not correct

Design

New <ActivitySendStatus> provider

A new context provider to provide UI-oriented information on outgoing activity send status. Its corresponding hook useSendStatusByActivityKey will retrieve a map of send status by activity key. Its return value has type of Map<string, 'sending' | 'send failed' | 'sent'>.

All existing code related to send status of outgoing activities are being refactored to use this new provider.

New code added to chat history live region will use this provider to make sure it has a consistent experience as the visible chat history.

This send status is different from the channelData['webchat:send-status']:

channelData['webchat:send-status'] useSendStatusByActivityKey
"sending" "sending" if not timed out, otherwise, "send failed"
"send failed" "send failed"
"sent" "sent"

Notes: timeout is based on styleOptions.sendTimeout or styleOptions.sendTimeoutForAttachments.

Web developers can change styleOptions to modify send timeout on-the-fly and the useSendStatusByActivityKey will reflect the change immediately.

New activity.channelData['webchat:send-status']

This is very similar to the existing activity.channelData.state with a few differences:

  • Prefixed with webchat: for better namespace
  • sending will become send failed if and only if:
    • Failed to send due to fatal error, such as network error
      • This is same as previous behavior
    • Timed out for a hardcoded 5 minutes
      • Previously, timed out for X minutes as defined in styleOptions.sendTimeout

activity.channelData.state === 'send failed' is not very helpful because:

  • If styleOptions.sendTimeout is increased, it will NOT revive the status back to sending because the postActivitySaga has already stopped
  • It did not consider prolonged send timeouts for activity with attachments
  • The previous field will continue to use the standard value (default 20s). That means, for activity with attachments, which has a timeout of 120s, will be prematurely marked as timed out after 20s

For the reasons above, we ignored both sending and send failed state. We only considered the sent and not-sent state

  • If it took more than 20s (or 120s) in the non-sent state, we will mark the activity as "send failed"
  • If the activity cannot be sent due to fatal error, such as network error, we CANNOT immediately mark it as send failed because we ignored the send failed state. The UI will need to wait for 20s to reflect the fatal error

The newer channel data field will speed up showing "Send failed" as soon as fatal error is detected.

This field is NOT recommended to be a factor of whether the "Send failed. Retry" prompt should be shown or not. Web developers should use the new <ActivitySendStatus> provider and its useSendStatusByActivityKey hook. This is because this channel data field is not based on styleOptions.sendTimeout. Thus, it is not a direct representation of whether the "Send failed. Retry" prompt should be shown or not.

Added test facility createDirectLineEmulator

Previously, we use createDirectLineWithTranscript and inject static transcript with activity.channelData.state. However, this approach is not truly reflecting the reality:

  • Outgoing activities should be tracked by postActivitySaga.ts
  • Chat adapter/service should not send any activities to Web Chat with channelData.state

Thus, we created a new createDirectLineEmulator:

  • For static transcript with bot activities only, we can continue to use createDirectLineWithTranscript, otherwise;
  • We should use createDirectLineEmulator instead

Fixing intermediate send status

Previously, when an outgoing activity is sent successfully, the activities reducer would receive the following actions:

  1. POST_ACTIVITY, which triggers the postActivitySaga
  2. POST_ACTIVITY_PENDING
  3. INCOMING_ACTIVITY, when an echo back is received from the chat adapter/service
  4. POST_ACTIVITY_FULFILLED

When INCOMING_ACTIVITY is processed by the activities reducer, it upsert an activity into the state with channelData.state unset. This is because the incoming activity from the chat adapter/service should not contain this channel data field.

However, after the INCOMING_ACTIVITY is handled by the take effect in postActivitySaga, the POST_ACTIVITY_PENDING is dispatched. The POST_ACTIVITY_PENDING set the channelData.state to 'sent' for the activity in the activities state.

Thus, the channelData.state transitioned from sending to unset to sent in a very short period of time.

The send status for outgoing activities should never be unset.

The following table explains differences between the current channelData.state and newer channelData['webchat:send-status'], in chronological order:

# Action processed state after process webchat:send-status after process
1 POST_ACTIVITY (Not in activities state) (Not in activities state)
2 POST_ACTIVITY_PENDING sending sending
3 INCOMING_ACTIVITY Unset sending
4 POST_ACTIVITY_FULFILLED sent sent

For backward compatibility, the channelData.state field has not been updated. Instead, the new channelData['webchat:send-status'] is updated so it will always have a value for all outgoing activities.

Specific Changes

(Please see design section of this pull request)

  • I have added tests and executed them locally
  • I have updated CHANGELOG.md
  • I have updated documentation

Review Checklist

This section is for contributors to review your work.

  • Accessibility reviewed (tab order, content readability, alt text, color contrast)
  • Browser and platform compatibilities reviewed
  • CSS styles reviewed (minimal rules, no z-index)
  • Documents reviewed (docs, samples, live demo)
  • Internationalization reviewed (strings, unit formatting)
  • package.json and package-lock.json reviewed
  • Security reviewed (no data URIs, check for nonce leak)
  • Tests reviewed (coverage, legitimacy)

CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
Co-authored-by: TJ Durnford <tjdford@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants