Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
- `styleOptions.hideUploadButton` is being deprecated in favor of `styleOptions.disableFileUpload`. The option will be removed on or after 2027-07-14
- `botframework-directlinespeech-sdk` no longer ponyfill `AbortController`, it is supported by modern browsers, in PR [#5530](https://github.com/microsoft/BotFramework-WebChat/pull/5530)
- `activityMiddleware` is being deprecated in favor of [`polymiddleware`](./docs/MIDDLEWARE.md). It will be removed on or after 2027-08-16, related to PR [#5515](https://github.com/microsoft/BotFramework-WebChat/pull/5515)
- `activityStatusMiddleware.nextVisibleActivity` and `activityStatusMiddleware.sameTimestampGroup` is removed after deprecation, in PR [#5565](https://github.com/microsoft/BotFramework-WebChat/issues/5565), by [@compulim](https://github.com/compulim)

### Added

Expand Down Expand Up @@ -291,6 +292,8 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
- `useSuggestedActions()` hook is being deprecated in favor of the `useSuggestedActionsHooks().useSuggestedActions()` hook, in PR [#5489](https://github.com/microsoft/BotFramework-WebChat/pull/5489), by [@compulim](https://github.com/compulim)
- Fixed core internal import in legacy CommonJS environments, in [5509](https://github.com/microsoft/BotFramework-WebChat/pull/5509), by [@OEvgeny](https://github.com/OEvgeny)
- `activityMiddleware` is being deprecated in favor of [`polymiddleware`](./docs/MIDDLEWARE.md). It will be removed on or after 2027-08-16, related to PR [#5515](https://github.com/microsoft/BotFramework-WebChat/pull/5515)
- `activityStatusMiddleware.nextVisibleActivity` and `activityStatusMiddleware.sameTimestampGroup` is being deprecated and will be removed on or after 2022-07-22, in PR [#4362](https://github.com/microsoft/BotFramework-WebChat/issues/4362), by [@compulim](https://github.com/compulim)
- Completed deprecation, in PR [#5565](https://github.com/microsoft/BotFramework-WebChat/issues/5565), by [@compulim](https://github.com/compulim)

### Samples

Expand Down
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/activityGrouping.css" rel="stylesheet" type="text/css" />
Expand Down Expand Up @@ -58,10 +58,10 @@
}
} = window;

const PasswordInputActivity = ({ activity, nextVisibleActivity }) => {
const PasswordInputActivity = ({ activity }) => {
const [twoFACode, setTwoFACode] = useState('');
const [submitted, setSubmitted] = useState(false);
const renderActivityStatus = useCreateActivityStatusRenderer()({ activity, nextVisibleActivity });
const renderActivityStatus = useCreateActivityStatusRenderer()({ activity });
const sendPostBack = useSendPostBack();

const handleCodeChange = useCallback(
Expand Down Expand Up @@ -101,15 +101,18 @@
);
};

const activityMiddleware = () => next => (...args) => {
const [{ activity, nextVisibleActivity }] = args;
const activityMiddleware =
() =>
next =>
(...args) => {
const [{ activity }] = args;

if (activity.type === 'password-input') {
return () => <PasswordInputActivity activity={activity} nextVisibleActivity={nextVisibleActivity} />;
}
if (activity.type === 'password-input') {
return () => <PasswordInputActivity activity={activity} />;
}

return next(...args);
};
return next(...args);
};

run(async function () {
const now = Date.now();
Expand Down
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 @@ -38,8 +38,8 @@
activity: {
from: { role }
},
sendState,
sameTimestampGroup
hideTimestamp,
sendState
}
] = args;

Expand All @@ -56,7 +56,7 @@
<span className="activity-status__send-failed">Send failed.</span>
</span>
);
} else if (!sameTimestampGroup) {
} else if (!hideTimestamp) {
return (
<span className="activity-status">
<span className="activity-status__timestamp-pretext">{role === 'user' ? 'User at ' : 'Bot at '}</span>
Expand Down
2 changes: 2 additions & 0 deletions docs/HOOKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,8 @@ This hook will return a function that, when called with a `Date` object, `number

## `useRenderActivity`

> Deprecation notes: this hook is being deprecated and replaced by `useBuildRenderActivityCallback`, it will be removed on or after 2027-08-16.

<!-- prettier-ignore-start -->
```js
useRenderActivity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ function createActivityPolymiddlewareFromLegacy(
);

return ({ activity }) => {
// TODO: [P1] `nextVisibleActivity` is deprecated and should be removed.
const legacyResult = legacyHandler({ activity, nextVisibleActivity: undefined as any });
const legacyResult = legacyHandler({ activity });

if (!legacyResult) {
// Legacy cannot fallback to poly middleware due to signature incompatibility.
Expand Down
1 change: 0 additions & 1 deletion packages/api-middleware/src/legacy/activityMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ type LegacyActivityComponent = (props: LegacyActivityProps) => Exclude<ReactNode

type LegacyActivityComponentFactoryOptions = {
activity: WebChatActivity;
nextVisibleActivity: WebChatActivity;
};

type LegacyActivityComponentFactory = (
Expand Down
46 changes: 13 additions & 33 deletions packages/api/src/hooks/useCreateActivityStatusRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
/* eslint react/prop-types: "off" */
/* eslint react/require-default-props: "off" */

import type { WebChatActivity } from 'botframework-webchat-core';
import { type WebChatActivity } from 'botframework-webchat-core';
Comment thread
compulim marked this conversation as resolved.
import React, { memo, useMemo, type ReactNode } from 'react';

import useGetKeyByActivity from '../hooks/useGetKeyByActivity';
import useSendStatusByActivityKey from '../hooks/useSendStatusByActivityKey';
import type { RenderActivityStatus } from '../types/ActivityStatusMiddleware';
import type { SendStatus } from '../types/SendStatus';
import { type RenderActivityStatus } from '../types/ActivityStatusMiddleware';
import { type SendStatus } from '../types/SendStatus';
Comment thread
compulim marked this conversation as resolved.
import useWebChatAPIContext from './internal/useWebChatAPIContext';

type ActivityStatusContainerCoreProps = Readonly<{
activity: WebChatActivity;
hideTimestamp: boolean;
nextVisibleActivity: WebChatActivity;
sendStatus: SendStatus;
}>;

const ActivityStatusContainerCore = memo(
({ activity, hideTimestamp, nextVisibleActivity, sendStatus }: ActivityStatusContainerCoreProps) => {
({ activity, hideTimestamp, sendStatus }: ActivityStatusContainerCoreProps) => {
const { activityStatusRenderer: createActivityStatusRenderer }: { activityStatusRenderer: RenderActivityStatus } =
useWebChatAPIContext();

return createActivityStatusRenderer({
activity,
hideTimestamp,
nextVisibleActivity, // "nextVisibleActivity" is for backward compatibility, please remove this line on or after 2022-07-22.
sameTimestampGroup: hideTimestamp, // "sameTimestampGroup" is for backward compatibility, please remove this line on or after 2022-07-22.
sendState: sendStatus === 'send failed' || sendStatus === 'sent' ? sendStatus : 'sending'
});
}
Expand All @@ -35,45 +32,28 @@ const ActivityStatusContainerCore = memo(
type ActivityStatusContainerProps = Readonly<{
activity: WebChatActivity;
hideTimestamp: boolean;
nextVisibleActivity: WebChatActivity;
}>;

const ActivityStatusContainer = memo(
({ activity, hideTimestamp, nextVisibleActivity }: ActivityStatusContainerProps) => {
const [sendStatusByActivityKey] = useSendStatusByActivityKey();
const getKeyByActivity = useGetKeyByActivity();
const ActivityStatusContainer = memo(({ activity, hideTimestamp }: ActivityStatusContainerProps) => {
const [sendStatusByActivityKey] = useSendStatusByActivityKey();
const getKeyByActivity = useGetKeyByActivity();

const key = getKeyByActivity(activity);
const key = getKeyByActivity(activity);

const sendStatus = (typeof key === 'string' && sendStatusByActivityKey.get(key)) || 'sent';
const sendStatus = (typeof key === 'string' && sendStatusByActivityKey.get(key)) || 'sent';

return (
<ActivityStatusContainerCore
activity={activity}
hideTimestamp={hideTimestamp}
nextVisibleActivity={nextVisibleActivity}
sendStatus={sendStatus}
/>
);
}
);
return <ActivityStatusContainerCore activity={activity} hideTimestamp={hideTimestamp} sendStatus={sendStatus} />;
});

export type ActivityStatusRenderer = (renderOptions: {
activity: WebChatActivity;
nextVisibleActivity: WebChatActivity;
}) => (props?: { hideTimestamp?: boolean }) => ReactNode;

export default function useCreateActivityStatusRenderer(): ActivityStatusRenderer {
return useMemo<ActivityStatusRenderer>(
() =>
({ activity, nextVisibleActivity }) =>
({ hideTimestamp } = {}) => (
<ActivityStatusContainer
activity={activity}
hideTimestamp={hideTimestamp}
nextVisibleActivity={nextVisibleActivity}
/>
),
({ activity }) =>
({ hideTimestamp } = {}) => <ActivityStatusContainer activity={activity} hideTimestamp={hideTimestamp} />,
[]
);
}
8 changes: 1 addition & 7 deletions packages/api/src/legacy/LegacyActivityBridge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,7 @@ function LegacyActivityBridge(props: LegacyActivityBridgeComponentProps) {
const renderAvatarRaw = useCreateAvatarRenderer();

const renderActivityStatus = useMemo(
() =>
createActivityStatusRenderer(
Object.freeze({
activity,
nextVisibleActivity: undefined
})
),
() => createActivityStatusRenderer(Object.freeze({ activity })),
[activity, createActivityStatusRenderer]
);
const renderAvatar = useMemo(
Expand Down
10 changes: 1 addition & 9 deletions packages/api/src/types/ActivityStatusMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ReactElement } from 'react';
import type { WebChatActivity } from 'botframework-webchat-core';
import type { ReactElement } from 'react';
Comment thread
compulim marked this conversation as resolved.

import type { SendStatus } from '../types/SendStatus';

Expand All @@ -8,14 +8,6 @@ type RenderActivityStatusOptions = {
activity: WebChatActivity;
hideTimestamp: boolean;
sendState: SendStatus;

// "nextVisibleActivity" is for backward compatibility, please remove this line on or after 2022-07-22.
/** @deprecated */
nextVisibleActivity: WebChatActivity;

// "sameTimestampGroup" is for backward compatibility, please remove this line on or after 2022-07-22.
/** @deprecated */
sameTimestampGroup: boolean;
};

type RenderActivityStatus = (options: RenderActivityStatusOptions) => ReactElement;
Expand Down
8 changes: 4 additions & 4 deletions samples/05.custom-components/f.password-input/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@

const { useCallback, useState } = window.React;

const PasswordInputActivity = ({ activity, nextVisibleActivity }) => {
const PasswordInputActivity = ({ activity }) => {
const [twoFACode, setTwoFACode] = useState('');
const [submitted, setSubmitted] = useState(false);
const renderActivityStatus = useCreateActivityStatusRenderer()({ activity, sendState: 'sent' });
Expand Down Expand Up @@ -140,13 +140,13 @@
const activityMiddleware =
() =>
next =>
({ activity, nextVisibleActivity, ...otherArgs }) => {
({ activity, ...otherArgs }) => {
const { name, type } = activity;

if (type === 'event' && name === 'passwordInput') {
return () => <PasswordInputActivity activity={activity} nextVisibleActivity={nextVisibleActivity} />;
return () => <PasswordInputActivity activity={activity} />;
} else {
return next({ activity, nextVisibleActivity, ...otherArgs });
return next({ activity, ...otherArgs });
}
};

Expand Down
6 changes: 3 additions & 3 deletions samples/05.custom-components/g.activity-status/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@
activity: {
from: { role }
},
sendState,
sameTimestampGroup
hideTimestamp,
sendState
} = args;

if (sendState === 'sending') {
return <span className="activityStatus activityStatus__sendStatus">Sending&hellip;</span>;
} else if (sendState === 'send failed') {
// Custom retry logic can be added when rendering the "Send failed." status.
return <span className="activityStatus">Send failed.</span>;
} else if (!sameTimestampGroup) {
} else if (!hideTimestamp) {
return (
<span className="activityStatus activityStatus__timestamp">
<span className="activityStatus__timestampPretext">{role === 'user' ? 'User at ' : 'Bot at '}</span>
Expand Down
Loading