Skip to content

Commit 0d1dab6

Browse files
Claudecompulim
andcommitted
Add hero card polymiddleware infrastructure
Agent-Logs-Url: https://github.com/compulim/BotFramework-WebChat/sessions/88a86eb4-5c4d-4bd5-a254-674adf9ab5e7 Co-authored-by: compulim <1622400+compulim@users.noreply.github.com>
1 parent cd39b84 commit 0d1dab6

10 files changed

Lines changed: 175 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Breaking changes in this release:
4444

4545
### Added
4646

47+
- Added hero card polymiddleware infrastructure to enable customization of hero card rendering, in PR [#XXXX](https://github.com/microsoft/BotFramework-WebChat/pull/XXXX), by [@compulim](https://github.com/compulim)
4748
- (Experimental) Added pre-chat message with starter prompts in Fluent UI, in PR [#5255](https://github.com/microsoft/BotFramework-WebChat/issues/5255) and [#5263](https://github.com/microsoft/BotFramework-WebChat/issues/5263), by [@compulim](https://github.com/compulim)
4849
- (Experimental) Added `isPrimary` props to Fluent UI send box. When set, will wire up with `useSendBoxValue` and works with starter prompts in pre-chat message, in PR [#5257](https://github.com/microsoft/BotFramework-WebChat/issues/5257), by [@compulim](https://github.com/compulim)
4950
- (Experimental) Expand Fluent theme support to activities and transcript, in PR [#5258](https://github.com/microsoft/BotFramework-WebChat/pull/5258), by [@OEvgeny](https://github.com/OEvgeny)

packages/api-middleware/src/PolymiddlewareComposer.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import { ActivityPolymiddlewareProvider, extractActivityEnhancer } from './activityPolymiddleware';
1818
import { AvatarPolymiddlewareProvider, extractAvatarEnhancer } from './avatarPolymiddleware';
1919
import { ErrorBoxPolymiddlewareProvider, extractErrorBoxEnhancer } from './errorBoxPolymiddleware';
20+
import { extractHeroCardEnhancer, HeroCardPolymiddlewareProvider } from './heroCardPolymiddleware';
2021
import { Polymiddleware } from './types/Polymiddleware';
2122

2223
const polymiddlewareComposerPropsSchema = pipe(
@@ -56,6 +57,13 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) {
5657

5758
const errorBoxPolymiddleware = useMemo(() => errorBoxEnhancers.map(enhancer => () => enhancer), [errorBoxEnhancers]);
5859

60+
const heroCardEnhancers = useMemoIterable<ReturnType<typeof extractHeroCardEnhancer>>(
61+
() => extractHeroCardEnhancer(polymiddleware),
62+
[polymiddleware]
63+
);
64+
65+
const heroCardPolymiddleware = useMemo(() => heroCardEnhancers.map(enhancer => () => enhancer), [heroCardEnhancers]);
66+
5967
// Didn't thoroughly think through this part yet, but I am using the first approach for now:
6068

6169
// 1. <XXXProvider> for every type of middleware
@@ -66,11 +74,15 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) {
6674
// - <Proxy> will need to be rebuilt, as it uses a different `useBuildRenderCallback()`
6775

6876
return (
69-
<ActivityPolymiddlewareProvider middleware={activityPolymiddleware}>
70-
<AvatarPolymiddlewareProvider middleware={avatarPolymiddleware}>
71-
<ErrorBoxPolymiddlewareProvider middleware={errorBoxPolymiddleware}>{children}</ErrorBoxPolymiddlewareProvider>
72-
</AvatarPolymiddlewareProvider>
73-
</ActivityPolymiddlewareProvider>
77+
<ErrorBoxPolymiddlewareProvider middleware={errorBoxPolymiddleware}>
78+
<ActivityPolymiddlewareProvider middleware={activityPolymiddleware}>
79+
<AvatarPolymiddlewareProvider middleware={avatarPolymiddleware}>
80+
<HeroCardPolymiddlewareProvider middleware={heroCardPolymiddleware}>
81+
{children}
82+
</HeroCardPolymiddlewareProvider>
83+
</AvatarPolymiddlewareProvider>
84+
</ActivityPolymiddlewareProvider>
85+
</ErrorBoxPolymiddlewareProvider>
7486
);
7587
}
7688

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
2+
import React, { memo, useMemo } from 'react';
3+
import { object, pipe, readonly, type InferInput } from 'valibot';
4+
5+
import templatePolymiddleware, {
6+
type InferHandler,
7+
type InferHandlerResult,
8+
type InferMiddleware,
9+
type InferProps,
10+
type InferProviderProps,
11+
type InferRenderer,
12+
type InferRequest
13+
} from './private/templatePolymiddleware';
14+
15+
const {
16+
createMiddleware: createHeroCardPolymiddleware,
17+
extractEnhancer: extractHeroCardEnhancer,
18+
Provider: HeroCardPolymiddlewareProvider,
19+
Proxy,
20+
reactComponent: heroCardComponent,
21+
useBuildRenderCallback: useBuildRenderHeroCardCallback
22+
} = templatePolymiddleware<{ readonly heroCard: unknown }, { readonly children?: never }>('HeroCard');
23+
24+
type HeroCardPolymiddleware = InferMiddleware<typeof HeroCardPolymiddlewareProvider>;
25+
type HeroCardPolymiddlewareHandler = InferHandler<typeof HeroCardPolymiddlewareProvider>;
26+
type HeroCardPolymiddlewareHandlerResult = InferHandlerResult<typeof HeroCardPolymiddlewareProvider>;
27+
type HeroCardPolymiddlewareProps = InferProps<typeof HeroCardPolymiddlewareProvider>;
28+
type HeroCardPolymiddlewareRenderer = InferRenderer<typeof HeroCardPolymiddlewareProvider>;
29+
type HeroCardPolymiddlewareRequest = InferRequest<typeof HeroCardPolymiddlewareProvider>;
30+
type HeroCardPolymiddlewareProviderProps = InferProviderProps<typeof HeroCardPolymiddlewareProvider>;
31+
32+
const HeroCardPolymiddlewareProxyPropsSchema = pipe(
33+
object({
34+
heroCard: object({})
35+
}),
36+
readonly()
37+
);
38+
39+
type HeroCardPolymiddlewareProxyProps = Readonly<InferInput<typeof HeroCardPolymiddlewareProxyPropsSchema>>;
40+
41+
// A friendlier version than the organic <Proxy>.
42+
const HeroCardPolymiddlewareProxy = memo(function HeroCardPolymiddlewareProxy(props: HeroCardPolymiddlewareProxyProps) {
43+
const { heroCard } = validateProps(HeroCardPolymiddlewareProxyPropsSchema, props);
44+
45+
const request = useMemo(() => ({ heroCard }), [heroCard]);
46+
47+
return <Proxy request={request} />;
48+
});
49+
50+
export {
51+
createHeroCardPolymiddleware,
52+
extractHeroCardEnhancer,
53+
heroCardComponent,
54+
HeroCardPolymiddlewareProvider,
55+
HeroCardPolymiddlewareProxy,
56+
useBuildRenderHeroCardCallback,
57+
type HeroCardPolymiddleware,
58+
type HeroCardPolymiddlewareHandler,
59+
type HeroCardPolymiddlewareHandlerResult,
60+
type HeroCardPolymiddlewareProps,
61+
type HeroCardPolymiddlewareProviderProps,
62+
type HeroCardPolymiddlewareProxyProps,
63+
type HeroCardPolymiddlewareRenderer,
64+
type HeroCardPolymiddlewareRequest
65+
};

packages/api-middleware/src/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ export {
4141
type ErrorBoxPolymiddlewareRequest
4242
} from './errorBoxPolymiddleware';
4343

44+
export {
45+
createHeroCardPolymiddleware,
46+
extractHeroCardEnhancer,
47+
heroCardComponent,
48+
HeroCardPolymiddlewareProxy,
49+
useBuildRenderHeroCardCallback,
50+
type HeroCardPolymiddleware,
51+
type HeroCardPolymiddlewareHandler,
52+
type HeroCardPolymiddlewareHandlerResult,
53+
type HeroCardPolymiddlewareProps,
54+
type HeroCardPolymiddlewareProxyProps,
55+
type HeroCardPolymiddlewareRenderer,
56+
type HeroCardPolymiddlewareRequest
57+
} from './heroCardPolymiddleware';
58+
4459
// TODO: [P0] Add tests for nesting `polymiddleware`.
4560
export { __INTERNAL_DO_NOT_USE__legacyAvatarMiddlewareOriginalRequestSymbol } from './legacy/avatarMiddleware';
4661
export { default as PolymiddlewareComposer } from './PolymiddlewareComposer';

packages/api/src/boot/middleware.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ export {
2727
type AvatarPolymiddlewareRequest
2828
} from '@msinternal/botframework-webchat-api-middleware';
2929

30+
export {
31+
createHeroCardPolymiddleware,
32+
heroCardComponent,
33+
type HeroCardPolymiddleware,
34+
type HeroCardPolymiddlewareHandler,
35+
type HeroCardPolymiddlewareHandlerResult,
36+
type HeroCardPolymiddlewareProps,
37+
type HeroCardPolymiddlewareRenderer,
38+
type HeroCardPolymiddlewareRequest
39+
} from '@msinternal/botframework-webchat-api-middleware';
40+
3041
export {
3142
default as AvatarPolymiddlewareProxy,
3243
AvatarPolymiddlewareProxyProps

packages/bundle/src/adaptiveCards/Attachment/HeroCardAttachment.js

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
2+
import React, { memo } from 'react';
3+
import { boolean, object, optional, pipe, readonly, type InferInput } from 'valibot';
4+
5+
import { HeroCardPolymiddlewareProxy } from '../../boot/actual/middleware';
6+
import { directLineBasicCardSchema } from './private/directLineSchema';
7+
8+
const heroCardAttachmentPropsSchema = pipe(
9+
object({
10+
attachment: pipe(
11+
object({
12+
content: directLineBasicCardSchema
13+
}),
14+
readonly()
15+
),
16+
disabled: optional(boolean())
17+
}),
18+
readonly()
19+
);
20+
21+
type HeroCardAttachmentProps = InferInput<typeof heroCardAttachmentPropsSchema>;
22+
23+
function HeroCardAttachment(props: HeroCardAttachmentProps) {
24+
const { attachment, disabled } = validateProps(heroCardAttachmentPropsSchema, props);
25+
26+
return attachment.content ? <HeroCardPolymiddlewareProxy heroCard={attachment.content} /> : null;
27+
}
28+
29+
HeroCardAttachment.displayName = 'HeroCardAttachment';
30+
31+
export default memo(HeroCardAttachment);

packages/bundle/src/boot/actual/middleware.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,17 @@ export {
4444
} from 'botframework-webchat-api/middleware';
4545

4646
export { createAvatarPolymiddlewareFromLegacy } from 'botframework-webchat-api/middleware';
47+
48+
export {
49+
createHeroCardPolymiddleware,
50+
heroCardComponent,
51+
HeroCardPolymiddlewareProxy,
52+
useBuildRenderHeroCardCallback,
53+
type HeroCardPolymiddleware,
54+
type HeroCardPolymiddlewareHandler,
55+
type HeroCardPolymiddlewareHandlerResult,
56+
type HeroCardPolymiddlewareProps,
57+
type HeroCardPolymiddlewareProxyProps,
58+
type HeroCardPolymiddlewareRenderer,
59+
type HeroCardPolymiddlewareRequest
60+
} from 'botframework-webchat-api/middleware';
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { createHeroCardPolymiddleware, heroCardComponent } from 'botframework-webchat-api/middleware';
2+
import React from 'react';
3+
4+
import HeroCardContent from '../adaptiveCards/Attachment/HeroCardContent';
5+
6+
function createDefaultHeroCardPolymiddleware() {
7+
return createHeroCardPolymiddleware(() => ({ heroCard }) =>
8+
heroCardComponent(HeroCardContent, { content: heroCard })
9+
);
10+
}
11+
12+
export default createDefaultHeroCardPolymiddleware;

packages/bundle/src/useComposerProps.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { AttachmentForScreenReaderMiddleware, AttachmentMiddleware } from 'botframework-webchat-api';
1+
import { AttachmentForScreenReaderMiddleware, AttachmentMiddleware, Polymiddleware } from 'botframework-webchat-api';
22
import { type HTMLContentTransformMiddleware } from 'botframework-webchat-component';
33
import { useMemo } from 'react';
44

55
import createAdaptiveCardsAttachmentForScreenReaderMiddleware from './adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware';
66
import createAdaptiveCardsAttachmentMiddleware from './adaptiveCards/createAdaptiveCardsAttachmentMiddleware';
77
import createAdaptiveCardsStyleSet from './adaptiveCards/Styles/createAdaptiveCardsStyleSet';
8+
import createDefaultHeroCardPolymiddleware from './heroCard/createDefaultHeroCardPolymiddleware';
89
import createHTMLContentTransformMiddleware from './markdown/createHTMLContentTransformMiddleware';
910
import defaultRenderMarkdown from './markdown/renderMarkdown';
1011

@@ -31,6 +32,7 @@ export default function useComposerProps({
3132
attachmentMiddleware: AttachmentMiddleware[];
3233
extraStyleSet: any;
3334
htmlContentTransformMiddleware: readonly HTMLContentTransformMiddleware[];
35+
polymiddleware: readonly Polymiddleware[];
3436
renderMarkdown: (
3537
markdown: string,
3638
newLineOptions: { markdownRespectCRLF: boolean },
@@ -63,11 +65,17 @@ export default function useComposerProps({
6365
[htmlContentTransformMiddleware]
6466
);
6567

68+
const polymiddleware = useMemo<readonly Polymiddleware[]>(
69+
() => Object.freeze([createDefaultHeroCardPolymiddleware()]),
70+
[]
71+
);
72+
6673
return Object.freeze({
6774
attachmentForScreenReaderMiddleware: patchedAttachmentForScreenReaderMiddleware,
6875
attachmentMiddleware: patchedAttachmentMiddleware,
6976
extraStyleSet,
7077
htmlContentTransformMiddleware: patchedHTMLContentTransformMiddleware,
78+
polymiddleware,
7179
renderMarkdown: patchedRenderMarkdown
7280
});
7381
}

0 commit comments

Comments
 (0)