Skip to content

Commit

Permalink
Livestreaming typing animation (#5141)
Browse files Browse the repository at this point in the history
* Rename typing indicator BEM class

* Add typingIndicator

* Add useAllTyping to replace typing reducer

* Fix useAllTyping

* Display typing activities

* Display typing activities

* Multiple activities under one activity key

* Clean up

* Use activity revisions instead of typing

* Add activityOrder test

* Fix activity order

* Fix tests

* Simplify

* Clean up

* Add simultaneous test

* Hide typing indicator for chunked

* Fix tests

* Clean up

* Add tests

* Clean up

* Add tests

* Clean up

* Clean up

* Clean up

* Add out-of-order test

* Fix out-of-order

* Reduce number of render

* Remove use-propagate

* Add comments

* Fix tests

* Fix tests

* Migrate test

* Fix test

* Add initial value

* Add channelData.styleOptions.typingIndicatorDuration

* Add entries

* Fix streamSequence for OOO

* Rename streamMode to streamType

* Add informative message

* Simplify typing structure

* Typo

* Add `type` property to `useActiveTyping`

* Rename "indicator" to "busy"

* Move reduceIterable to /hooks

* Fix tests

* Add useActiveTyping test for livestream

* Fix typing structure and add snapshots

* isLivestream should return false for informative message

* Add snapshot

* Clean up

* Clean up

* Add comment

* Add livestream related channelData to "message" activity

* Add comment

* Clean up isPartOfLivestreamSession

* Add notable changes

* Explain semver

* Rename to isLivestreamChunk

* Rename to "receivedAt" from "appearAt"

* Add docs

* Better grammar

* Add undefined

* Add more hooks

* Typo

* Memoize empty array
  • Loading branch information
compulim authored May 15, 2024
1 parent b00b5c9 commit 68b8b71
Show file tree
Hide file tree
Showing 101 changed files with 2,730 additions and 217 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Resolves [#XXX](https://github.com/microsoft/BotFramework-WebChat/issues/XXX). Added something, by [@johndoe](https://github.com/johndoe), in PR [#XXX](https://github.com/microsoft/BotFramework-WebChat/pull/XXX)
-->

Notes: web developers are advised to use [`~` (tilde range)](https://github.com/npm/node-semver?tab=readme-ov-file#tilde-ranges-123-12-1) to select minor versions, which contains new features and/or fixes. Use [`^` (caret range)](https://github.com/npm/node-semver?tab=readme-ov-file#caret-ranges-123-025-004) to select major versions, which may contains breaking changes.

## [Unreleased]

### Fixed
Expand Down Expand Up @@ -122,6 +124,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added `useMakeThumbnail` hook option to create a thumbnail from the file given, by [@compulim](https://github.com/compulim), in PR [#5123](https://github.com/microsoft/BotFramework-WebChat/pull/5123) and [#5122](https://github.com/microsoft/BotFramework-WebChat/pull/5122)
- Added `moduleFormat` and `transpiler` build info to `<meta>` tag, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
- Added support of rendering HTML-in-Markdown, in PR [#5161](https://github.com/microsoft/BotFramework-WebChat/pull/5161) and PR [#5164](https://github.com/microsoft/BotFramework-WebChat/pull/5164), by [@compulim](https://github.com/compulim), [@beyackle2](https://github.com/beyackle2), and [@OEvgeny](https://github.com/OEvgeny)
- Resolves [#5184](https://github.com/microsoft/BotFramework-WebChat/issues/5184). Added `channelData.webChat.styleOptions.typingIndicatorDuration` to override the default typing indicator duration on a per-activity basis, by [@compulim](https://github.com/compulim), in PR [#5141](https://github.com/microsoft/BotFramework-WebChat/pull/5141)
- Resolves [#4876](https://github.com/microsoft/BotFramework-WebChat/issues/4876) and [#4939](https://github.com/microsoft/BotFramework-WebChat/issues/4939). Added support of livestreaming, by [@compulim](https://github.com/compulim), in PR [#5141](https://github.com/microsoft/BotFramework-WebChat/pull/5141)

### Fixed

Expand Down
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,29 @@ Web Chat supports [Content Security Policy (CSP)](https://developer.mozilla.org/

> This section points out important version notes. For further information, please see the related links and check the [`CHANGELOG.md`](https://github.com/microsoft/BotFramework-WebChat/blob/main/CHANGELOG.md)
### 4.17.0 notable changes
Notes: web developers are advised to use [`~` (tilde range)](https://github.com/npm/node-semver?tab=readme-ov-file#tilde-ranges-123-12-1) to select minor versions, which contains new features and/or fixes. Use [`^` (caret range)](https://github.com/npm/node-semver?tab=readme-ov-file#caret-ranges-123-025-004) to select major versions, which may contains breaking changes.

#### Debut of ES Modules
## 4.18.0 notable changes

### Support livestreaming response

Bots can now livestream their responses. Before Bot Framework SDK support this feature, bot authors can follow the details in [this pull request](https://github.com/microsoft/BotFramework-WebChat/pull/5141) to construct the livestream responses.

## 4.17.0 notable changes

### Debut of ES Modules

Web Chat now exports as ES Modules (named exports) along with CommonJS (named and unnamed exports).

#### Improvement to file upload experience
### Improvement to file upload experience

End-user can now add a message and confirm before uploading their file to the bot. To opt-out of the new experience, pass `sendAttachmentOn: 'send'` in style options.

#### Theme pack support
### Theme pack support

We are excited to add theme pack support. Developers can now pack all their customization in a single package and publish it to NPM.

#### Experimental Fluent UI theme pack
### Experimental Fluent UI theme pack

We are excited to announce Fluent UI theme pack is in the work and is currently in experimental phase. This theme pack is designed for web developers who want to bring a native Copilot user experience to their customers.

Expand All @@ -60,15 +68,15 @@ Web Chat will now render HTML-in-Markdown. We have ported our sanitizer and acce

You can turn off this option by setting `styleOptions.markdownRenderHTML` to `false`.

### 4.16.1 notable changes
## 4.16.1 notable changes

Web Chat now supports [Adaptive Cards schema up to 1.6](https://adaptivecards.io/explorer/). Some features in Adaptive Cards are in preview or designed to use outside of Bot Framework. Web Chat does not support these features.

### 4.16.0 notable changes
## 4.16.0 notable changes

Starting from 4.16.0, Internet Explorer is no longer supported. After more than a year of the Internet Explorer 11 officially retirement, we decided to stop supporting Internet Explorer. This will help us to bring new features to Web Chat. 4.15.9 is the last version which supports Internet Explorer in limited fashion.

### 4.12.1 patch: New style property `adaptiveCardsParserMaxVersion`
## 4.12.1 patch: New style property `adaptiveCardsParserMaxVersion`

Web Chat 4.12.1 patch includes a new style property allowing developers to choose the max Adaptive Cards schema version. See [PR #3778](https://github.com/microsoft/BotFramework-WebChat/pull/3778) for code changes.

Expand Down Expand Up @@ -220,7 +228,7 @@ See the working sample of the [full Web Chat bundle](https://github.com/microsof

For full customizability, you can use React to recompose components of Web Chat.

To install the production build from NPM, run `npm install botframework-webchat`.
To install the production build from NPM, run `npm install botframework-webchat`. See our [version notes](#version-notes) on how to select a version.

<!-- prettier-ignore-start -->
```js
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 0 additions & 47 deletions __tests__/hooks/useTypingIndicatorVisible.js

This file was deleted.

21 changes: 10 additions & 11 deletions __tests__/html/hooks.useActiveTyping.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
Expand Down Expand Up @@ -63,7 +63,8 @@
at: 600,
expireAt: 5600,
name: expect.any(String),
role: 'user'
role: 'user',
type: 'busy'
}
]);

Expand All @@ -80,31 +81,29 @@
at: 600,
expireAt: 5600,
name: expect.any(String),
role: 'bot'
role: 'bot',
type: 'busy'
}
]);

// WHEN: The user type ".".
await pageObjects.typeInSendBox('.');

// THEN: `useActiveTyping` should return both.
await expect(
renderWithFunction(() =>
Object
.values(useActiveTyping()[0])
)
).resolves.toEqual([
await expect(renderWithFunction(() => Object.values(useActiveTyping()[0]))).resolves.toEqual([
{
at: 600,
expireAt: 5600,
name: expect.any(String),
role: 'bot'
role: 'bot',
type: 'busy'
},
{
at: 600,
expireAt: 5600,
name: expect.any(String),
role: 'user'
role: 'user',
type: 'busy'
}
]);
});
Expand Down
82 changes: 82 additions & 0 deletions __tests__/html/hooks.useActiveTyping.livestream.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="text/babel" data-presets="env,stage-3,react">
const {
WebChat: {
Components: { BasicWebChat, Composer },
hooks: { useActiveTyping }
}
} = window;

run(async function () {
const clock = lolex.createClock();

const { directLine, store } = testHelpers.createDirectLineEmulator({ ponyfill: clock });

const RunFunction = ({ fn }) => {
fn();

return false;
};

const renderWithFunction = fn =>
new Promise(resolve =>
ReactDOM.render(
<Composer directLine={directLine} ponyfill={clock} sendTypingIndicator={true} store={store}>
<BasicWebChat />
<RunFunction fn={() => resolve(fn && fn())} key={Date.now() + ''} />
</Composer>,
document.getElementById('webchat')
)
);

await renderWithFunction();

await pageConditions.webChatRendered();

clock.tick(600);

// WHEN: At initial.
await pageConditions.uiConnected();

// THEN: `useActiveTyping` should return nothing.
await expect(renderWithFunction(() => useActiveTyping())).resolves.toEqual([{}]);

// WHEN: Bot livestream.
await directLine.emulateIncomingActivity({
from: {
id: 'bot',
name: 'Bot',
role: 'bot'
},
text: 'Et est esse aliqua culpa quis laboris exercitation voluptate dolor.',
type: 'typing'
});

// THEN: `useActiveTyping` should return the livestream from the bot.
await expect(renderWithFunction(() => useActiveTyping())).resolves.toEqual([
{
bot: {
at: 600,
expireAt: 5600,
name: 'Bot',
role: 'bot',
type: 'livestream'
}
}
]);
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions __tests__/html/hooks.useActiveTyping.livestream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */

describe('useActiveTyping', () => {
test('should get bot livestream', () => runHTML('hooks.useActiveTyping.livestream.html'));
});
11 changes: 7 additions & 4 deletions __tests__/html/hooks.useActiveTyping.variable.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
Expand Down Expand Up @@ -63,7 +63,8 @@
at: 600,
expireAt: 5600,
name: expect.any(String),
role: 'user'
role: 'user',
type: 'busy'
}
]);

Expand All @@ -88,7 +89,8 @@
at: 600,
expireAt: 8610,
name: expect.any(String),
role: 'user'
role: 'user',
type: 'busy'
}
]);

Expand All @@ -104,7 +106,8 @@
at: 600,
expireAt: 13610,
name: expect.any(String),
role: 'user'
role: 'user',
type: 'busy'
}
]);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="text/babel">
run(async function () {
const {
ReactDOM: { render },
WebChat: {
Components: { BasicWebChat, Composer },
hooks: { useTypingIndicatorVisible }
}
} = window; // Imports in UMD fashion.

const directLine = WebChat.createDirectLine({ token: await testHelpers.token.fetchDirectLineToken() });
const store = testHelpers.createStore();

let typingIndicatorVisible;

const RunFunction = () => {
[typingIndicatorVisible] = useTypingIndicatorVisible();

return false;
};

render(
<Composer directLine={directLine} store={store}>
<BasicWebChat />
<RunFunction />
</Composer>,
document.getElementById('webchat')
);

await pageConditions.uiConnected();
await pageObjects.sendMessageViaSendBox('typing 1');
await pageConditions.numActivitiesShown(2);

expect(typingIndicatorVisible).toBe(true);
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */

describe('useTypingIndicatorVisible', () => {
test('getter should return true when typing indicator is shown', () => runHTML('hooks/useTypingIndicatorVisible/getter.botTyping.html'));
});
Loading

0 comments on commit 68b8b71

Please sign in to comment.