import * as React from 'react'; import * as AdaptiveCards from "microsoft-adaptivecards"; import * as AdaptiveCardSchema from "microsoft-adaptivecards/built/schema"; import { CardAction } from "ambit-directlinejs/built/directLine"; import { classList, IDoCardAction, konsole } from "./Chat"; import { AjaxResponse, AjaxRequest } from 'rxjs/observable/dom/AjaxObservable'; import * as adaptivecardsHostConfig from '../adaptivecards-hostconfig.json'; export interface Props { card: AdaptiveCardSchema.ICard, onImageLoad?: () => any, onClick?: (e: React.MouseEvent) => void, onCardAction: IDoCardAction, className?: string } export interface State { errors?: string[] } class LinkedAdaptiveCard extends AdaptiveCards.AdaptiveCard { constructor(public adaptiveCardContainer: AdaptiveCardContainer) { super(); } } export interface BotFrameworkCardAction extends CardAction { __isBotFrameworkCardAction: boolean } function getLinkedAdaptiveCard(action: AdaptiveCards.Action) { let element = action.parent; while (element && !(element instanceof LinkedAdaptiveCard)) { element = element.parent; } return element as LinkedAdaptiveCard; } function cardWithoutHttpActions(card: AdaptiveCardSchema.ICard) { if (!card.actions) return card; const actions: AdaptiveCardSchema.IActionBase[] = []; card.actions.forEach(action => { //filter out http action buttons if (action.type === 'Action.Http') return; if (action.type === 'Action.ShowCard') { const showCardAction = action as AdaptiveCardSchema.IActionShowCard; showCardAction.card = cardWithoutHttpActions(showCardAction.card); } actions.push(action); }); return { ...card, actions }; } AdaptiveCards.AdaptiveCard.onExecuteAction = (action: AdaptiveCards.ExternalAction) => { if (action instanceof AdaptiveCards.OpenUrlAction) { window.open(action.url); } else if (action instanceof AdaptiveCards.SubmitAction) { const linkedAdaptiveCard = getLinkedAdaptiveCard(action); if (linkedAdaptiveCard && action.data !== undefined) { if (typeof action.data === 'object' && (action.data as BotFrameworkCardAction).__isBotFrameworkCardAction) { const cardAction = (action.data as BotFrameworkCardAction); linkedAdaptiveCard.adaptiveCardContainer.onCardAction(cardAction.type, cardAction.value); } else { linkedAdaptiveCard.adaptiveCardContainer.onCardAction(typeof action.data === 'string' ? 'imBack' : 'postBack', action.data); } } } }; export class AdaptiveCardContainer extends React.Component { private div: HTMLDivElement; constructor(props: Props) { super(props); } public onCardAction: IDoCardAction = (type, value) => { this.props.onCardAction(type, value); } private onClick(e: React.MouseEvent) { if (!this.props.onClick) return; //do not allow form elements to trigger a parent click event switch ((e.target as HTMLElement).tagName) { case 'A': case 'AUDIO': case 'VIDEO': case 'BUTTON': case 'INPUT': case 'LABEL': case 'TEXTAREA': case 'SELECT': break; default: this.props.onClick(e); } } componentDidMount() { const adaptiveCard = new LinkedAdaptiveCard(this); adaptiveCard.parse(cardWithoutHttpActions(this.props.card)); const errors = adaptiveCard.validate(); if (errors.length === 0) { let renderedCard: HTMLElement; try { renderedCard = adaptiveCard.render(); } catch (e) { const ve: AdaptiveCards.IValidationError = { error: -1, message: e }; errors.push(ve); if (e.stack) { ve.message += '\n' + e.stack; } } if (renderedCard) { if (this.props.onImageLoad) { var imgs = renderedCard.querySelectorAll('img'); if (imgs && imgs.length > 0) { Array.prototype.forEach.call(imgs, (img: HTMLImageElement) => { img.addEventListener('load', this.props.onImageLoad); }); } } this.div.appendChild(renderedCard); return; } } if (errors.length > 0) { console.log('Error(s) rendering AdaptiveCard:'); errors.forEach(e => console.log(e.message)); this.setState({ errors: errors.map(e => e.message) }); } } render() { let wrappedChildren: JSX.Element; const hasErrors = this.state && this.state.errors && this.state.errors.length > 0; if (hasErrors) { wrappedChildren = (
Can't render card
); } else if (this.props.children) { wrappedChildren = (
{this.props.children}
); } else { wrappedChildren = null; } return (
this.div = div} onClick={e => this.onClick(e)}> {wrappedChildren}
) } componentDidUpdate() { if (this.props.onImageLoad) this.props.onImageLoad(); } } AdaptiveCards.setHostConfig(adaptivecardsHostConfig);