Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Breaking changes in this release:

### Added

- 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)
- (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)
- (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)
- (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)
Expand Down
22 changes: 17 additions & 5 deletions packages/api-middleware/src/PolymiddlewareComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { ActivityPolymiddlewareProvider, extractActivityEnhancer } from './activityPolymiddleware';
import { AvatarPolymiddlewareProvider, extractAvatarEnhancer } from './avatarPolymiddleware';
import { ErrorBoxPolymiddlewareProvider, extractErrorBoxEnhancer } from './errorBoxPolymiddleware';
import { extractHeroCardEnhancer, HeroCardPolymiddlewareProvider } from './heroCardPolymiddleware';
import { Polymiddleware } from './types/Polymiddleware';

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

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

const heroCardEnhancers = useMemoIterable<ReturnType<typeof extractHeroCardEnhancer>>(
() => extractHeroCardEnhancer(polymiddleware),
[polymiddleware]
);

const heroCardPolymiddleware = useMemo(() => heroCardEnhancers.map(enhancer => () => enhancer), [heroCardEnhancers]);

// Didn't thoroughly think through this part yet, but I am using the first approach for now:

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

return (
<ActivityPolymiddlewareProvider middleware={activityPolymiddleware}>
<AvatarPolymiddlewareProvider middleware={avatarPolymiddleware}>
<ErrorBoxPolymiddlewareProvider middleware={errorBoxPolymiddleware}>{children}</ErrorBoxPolymiddlewareProvider>
</AvatarPolymiddlewareProvider>
</ActivityPolymiddlewareProvider>
<ErrorBoxPolymiddlewareProvider middleware={errorBoxPolymiddleware}>
<ActivityPolymiddlewareProvider middleware={activityPolymiddleware}>
<AvatarPolymiddlewareProvider middleware={avatarPolymiddleware}>
<HeroCardPolymiddlewareProvider middleware={heroCardPolymiddleware}>
{children}
</HeroCardPolymiddlewareProvider>
</AvatarPolymiddlewareProvider>
</ActivityPolymiddlewareProvider>
</ErrorBoxPolymiddlewareProvider>
);
}

Expand Down
65 changes: 65 additions & 0 deletions packages/api-middleware/src/heroCardPolymiddleware.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
import React, { memo, useMemo } from 'react';
import { object, pipe, readonly, type InferInput } from 'valibot';

import templatePolymiddleware, {
type InferHandler,
type InferHandlerResult,
type InferMiddleware,
type InferProps,
type InferProviderProps,
type InferRenderer,
type InferRequest
} from './private/templatePolymiddleware';

const {
createMiddleware: createHeroCardPolymiddleware,
extractEnhancer: extractHeroCardEnhancer,
Provider: HeroCardPolymiddlewareProvider,
Proxy,
reactComponent: heroCardComponent,
useBuildRenderCallback: useBuildRenderHeroCardCallback
} = templatePolymiddleware<{ readonly heroCard: unknown }, { readonly children?: never }>('HeroCard');

type HeroCardPolymiddleware = InferMiddleware<typeof HeroCardPolymiddlewareProvider>;
type HeroCardPolymiddlewareHandler = InferHandler<typeof HeroCardPolymiddlewareProvider>;
type HeroCardPolymiddlewareHandlerResult = InferHandlerResult<typeof HeroCardPolymiddlewareProvider>;
type HeroCardPolymiddlewareProps = InferProps<typeof HeroCardPolymiddlewareProvider>;
type HeroCardPolymiddlewareRenderer = InferRenderer<typeof HeroCardPolymiddlewareProvider>;
type HeroCardPolymiddlewareRequest = InferRequest<typeof HeroCardPolymiddlewareProvider>;
type HeroCardPolymiddlewareProviderProps = InferProviderProps<typeof HeroCardPolymiddlewareProvider>;

const HeroCardPolymiddlewareProxyPropsSchema = pipe(
object({
heroCard: object({})
}),
readonly()
);

type HeroCardPolymiddlewareProxyProps = Readonly<InferInput<typeof HeroCardPolymiddlewareProxyPropsSchema>>;

// A friendlier version than the organic <Proxy>.
const HeroCardPolymiddlewareProxy = memo(function HeroCardPolymiddlewareProxy(props: HeroCardPolymiddlewareProxyProps) {
const { heroCard } = validateProps(HeroCardPolymiddlewareProxyPropsSchema, props);

const request = useMemo(() => ({ heroCard }), [heroCard]);

return <Proxy request={request} />;
});

export {
createHeroCardPolymiddleware,
extractHeroCardEnhancer,
heroCardComponent,
HeroCardPolymiddlewareProvider,
HeroCardPolymiddlewareProxy,
useBuildRenderHeroCardCallback,
type HeroCardPolymiddleware,
type HeroCardPolymiddlewareHandler,
type HeroCardPolymiddlewareHandlerResult,
type HeroCardPolymiddlewareProps,
type HeroCardPolymiddlewareProviderProps,
type HeroCardPolymiddlewareProxyProps,
type HeroCardPolymiddlewareRenderer,
type HeroCardPolymiddlewareRequest
};
15 changes: 15 additions & 0 deletions packages/api-middleware/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ export {
type ErrorBoxPolymiddlewareRequest
} from './errorBoxPolymiddleware';

export {
createHeroCardPolymiddleware,
extractHeroCardEnhancer,
heroCardComponent,
HeroCardPolymiddlewareProxy,
useBuildRenderHeroCardCallback,
type HeroCardPolymiddleware,
type HeroCardPolymiddlewareHandler,
type HeroCardPolymiddlewareHandlerResult,
type HeroCardPolymiddlewareProps,
type HeroCardPolymiddlewareProxyProps,
type HeroCardPolymiddlewareRenderer,
type HeroCardPolymiddlewareRequest
} from './heroCardPolymiddleware';

// TODO: [P0] Add tests for nesting `polymiddleware`.
export { __INTERNAL_DO_NOT_USE__legacyAvatarMiddlewareOriginalRequestSymbol } from './legacy/avatarMiddleware';
export { default as PolymiddlewareComposer } from './PolymiddlewareComposer';
Expand Down
14 changes: 14 additions & 0 deletions packages/api/src/boot/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ export {
type AvatarPolymiddlewareRequest
} from '@msinternal/botframework-webchat-api-middleware';

export {
createHeroCardPolymiddleware,
HeroCardPolymiddlewareProxy,
heroCardComponent,
useBuildRenderHeroCardCallback,
type HeroCardPolymiddleware,
type HeroCardPolymiddlewareHandler,
type HeroCardPolymiddlewareHandlerResult,
type HeroCardPolymiddlewareProps,
type HeroCardPolymiddlewareProxyProps,
type HeroCardPolymiddlewareRenderer,
type HeroCardPolymiddlewareRequest
} from '@msinternal/botframework-webchat-api-middleware';

export {
default as AvatarPolymiddlewareProxy,
AvatarPolymiddlewareProxyProps
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
import React, { memo } from 'react';
import { boolean, object, optional, pipe, readonly, type InferInput } from 'valibot';

import { HeroCardPolymiddlewareProxy } from '../../boot/actual/middleware';
import { directLineBasicCardSchema } from './private/directLineSchema';

const heroCardAttachmentPropsSchema = pipe(
object({
attachment: pipe(
object({
content: directLineBasicCardSchema
}),
readonly()
),
disabled: optional(boolean())
}),
readonly()
);

type HeroCardAttachmentProps = InferInput<typeof heroCardAttachmentPropsSchema>;

function HeroCardAttachment(props: HeroCardAttachmentProps) {
const { attachment, disabled } = validateProps(heroCardAttachmentPropsSchema, props);

return attachment.content ? <HeroCardPolymiddlewareProxy heroCard={attachment.content} /> : null;
}

HeroCardAttachment.displayName = 'HeroCardAttachment';

export default memo(HeroCardAttachment);
14 changes: 14 additions & 0 deletions packages/bundle/src/boot/actual/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,17 @@ export {
} from 'botframework-webchat-api/middleware';

export { createAvatarPolymiddlewareFromLegacy } from 'botframework-webchat-api/middleware';

export {
createHeroCardPolymiddleware,
heroCardComponent,
HeroCardPolymiddlewareProxy,
useBuildRenderHeroCardCallback,
type HeroCardPolymiddleware,
type HeroCardPolymiddlewareHandler,
type HeroCardPolymiddlewareHandlerResult,
type HeroCardPolymiddlewareProps,
type HeroCardPolymiddlewareProxyProps,
type HeroCardPolymiddlewareRenderer,
type HeroCardPolymiddlewareRequest
} from 'botframework-webchat-api/middleware';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createHeroCardPolymiddleware, heroCardComponent } from 'botframework-webchat-api/middleware';
import React from 'react';

import HeroCardContent from '../adaptiveCards/Attachment/HeroCardContent';

function createDefaultHeroCardPolymiddleware() {
return createHeroCardPolymiddleware(() => ({ heroCard }) =>
heroCardComponent(HeroCardContent, { content: heroCard })
);
}

export default createDefaultHeroCardPolymiddleware;
10 changes: 9 additions & 1 deletion packages/bundle/src/useComposerProps.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { AttachmentForScreenReaderMiddleware, AttachmentMiddleware } from 'botframework-webchat-api';
import { AttachmentForScreenReaderMiddleware, AttachmentMiddleware, Polymiddleware } from 'botframework-webchat-api';
import { type HTMLContentTransformMiddleware } from 'botframework-webchat-component';
import { useMemo } from 'react';

import createAdaptiveCardsAttachmentForScreenReaderMiddleware from './adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware';
import createAdaptiveCardsAttachmentMiddleware from './adaptiveCards/createAdaptiveCardsAttachmentMiddleware';
import createAdaptiveCardsStyleSet from './adaptiveCards/Styles/createAdaptiveCardsStyleSet';
import createDefaultHeroCardPolymiddleware from './heroCard/createDefaultHeroCardPolymiddleware';
import createHTMLContentTransformMiddleware from './markdown/createHTMLContentTransformMiddleware';
import defaultRenderMarkdown from './markdown/renderMarkdown';

Expand All @@ -31,6 +32,7 @@ export default function useComposerProps({
attachmentMiddleware: AttachmentMiddleware[];
extraStyleSet: any;
htmlContentTransformMiddleware: readonly HTMLContentTransformMiddleware[];
polymiddleware: readonly Polymiddleware[];
renderMarkdown: (
markdown: string,
newLineOptions: { markdownRespectCRLF: boolean },
Expand Down Expand Up @@ -63,11 +65,17 @@ export default function useComposerProps({
[htmlContentTransformMiddleware]
);

const polymiddleware = useMemo<readonly Polymiddleware[]>(
() => Object.freeze([createDefaultHeroCardPolymiddleware()]),
[]
);

return Object.freeze({
attachmentForScreenReaderMiddleware: patchedAttachmentForScreenReaderMiddleware,
attachmentMiddleware: patchedAttachmentMiddleware,
extraStyleSet,
htmlContentTransformMiddleware: patchedHTMLContentTransformMiddleware,
polymiddleware,
renderMarkdown: patchedRenderMarkdown
});
}
Loading