Skip to content

Commit

Permalink
Update packages/teams-js/src/public/pages.ts
Browse files Browse the repository at this point in the history
Co-authored-by: Trevor Harris <trharris@microsoft.com>
  • Loading branch information
2 people authored and Maggie G committed Feb 15, 2024
1 parent 58d26f2 commit 86a313d
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 51 deletions.
35 changes: 28 additions & 7 deletions apps/teams-test-app/src/components/PagesResponseButtonAPIs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,31 @@ const ShowResponseButton = (): React.ReactElement =>
}
},
submit: async (input) => {
const result = await pages.responseButton.showResponseButton(input);
return JSON.stringify(result);
await pages.responseButton.showResponseButton(input);
return 'Completed';
},
},
defaultInput: JSON.stringify({
responseId: 'reply',
actionInfo: {
actionId: 'actionId',
actionObjects: [
{
itemId: '1',
secondaryId: {
name: 'driveId',
value: 'secondaryDriveValue',
},
originalSource: {
messageId: 'mockMessageId',
conversationId: 'mockConversationId',
type: 'email',
},
type: 'm365content',
},
],
},
}),
});

const HideResponseButton = (): React.ReactElement =>
Expand All @@ -31,12 +52,12 @@ const HideResponseButton = (): React.ReactElement =>
},
});

const RegisterResponseButtonEventHandler = (): React.ReactElement =>
const RegisterResponseButtonClickEventHandler = (): React.ReactElement =>
ApiWithoutInput({
name: 'registerResponseButtonEventHandler',
title: 'Register Response Button Event Handler',
name: 'registerResponseButtonClickEventHandler',
title: 'Register Response Button Click Event Handler',
onClick: async (setResult) => {
pages.responseButton.responseButtonEventHandler((): void => {
pages.responseButton.registerResponseButtonClickEventHandler((): void => {
setResult('responseButtonEventHandler successfully called');
});
return 'Completed';
Expand All @@ -55,7 +76,7 @@ const PagesResponseButtonAPIs = (): ReactElement => (
<ModuleWrapper title="Response Button">
<ShowResponseButton />
<HideResponseButton />
<RegisterResponseButtonEventHandler />
<RegisterResponseButtonClickEventHandler />
<CheckPagesResponseButtonCapability />
</ModuleWrapper>
);
Expand Down
5 changes: 4 additions & 1 deletion packages/teams-js/src/internal/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,10 @@ export enum ApiName {
Pages_ShareDeepLink = 'pages.shareDeepLink',
Pages_ResponseButton_ShowResponseButton = 'pages.responseButton.showResponseButton',
Pages_ResponseButton_HideResponseButton = 'pages.responseButton.hideResponseButton',
Pages_ResponseButton_ResponseButtonEventHandler = 'pages.responseButton.responseButtonEventHandler',
Pages_ResponseButton_RegisterResponseButton = 'pages.responseButton.registerResponseButton',
Pages_ResponseButton_RegisterResponseButtonClickEventHandler = 'pages.responseButton.registerResponseButtonClickEventHandler',
Pages_ResponseButton_RegisterResponsButtonClickEvent_Failure = 'pages.responseButton.registerResponseButtonClickEvent.failure',
Pages_ResponseButton_RegisterResponsButtonClickEvent_Success = 'pages.responseButton.registerResponseButtonClickEvent.success',
Pages_Tabs_GetMruTabInstances = 'pages.tabs.getMruTabInstances',
Pages_Tabs_GetTabInstances = 'pages.tabs.getTabInstances',
Pages_Tabs_NavigateToTab = 'pages.tabs.navigateToTab',
Expand Down
3 changes: 3 additions & 0 deletions packages/teams-js/src/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ export {
DeepLinkParameters,
DialogInfo,
DialogSize,
EmailOriginalSource,
ErrorCode,
FileOpenPreference,
FrameContext,
FrameInfo,
LoadContext,
LocaleInfo,
M365ContentAction,
OriginalSource,
OriginalSourceType,
ResumeContext,
SdkError,
SecondaryId,
Expand Down
37 changes: 37 additions & 0 deletions packages/teams-js/src/public/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ export interface M365ContentAction extends BaseActionObject<ActionObjectType.M36
itemId: string;
/** Represents an optional secondary identifier for an action in a Microsoft 365 content item. */
secondaryId?: SecondaryId;

/**
* Indicates the original source of the action object.
*/
originalSource?: OriginalSource<OriginalSourceType>;
}

/**
Expand Down Expand Up @@ -261,6 +266,38 @@ export enum SecondaryM365ContentIdName {
UserId = 'userId',
}

/**
* The original source of the action object from
*
* @param T The type of original source
*
* @beta
*/
export interface OriginalSource<T extends OriginalSourceType> {
/** Represents original source type. */
type: T;
}
/**
* The properties of where the action objects original from
*
* @beta
*/
export enum OriginalSourceType {
/** Represents that the original source is from an email. */
Email = 'email',
}
/**
* Stores information necessary to represent the related email and to group the attachments.
*
* @beta
*/
export interface EmailOriginalSource extends OriginalSource<OriginalSourceType.Email> {
/** The exact message where the action was invoked from */
messageId: string;
/** The conversation where the action was invoked from */
conversationId: string;
}

/**
* Information common to all actions
*
Expand Down
168 changes: 141 additions & 27 deletions packages/teams-js/src/public/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { prefetchOriginsFromCDN } from '../internal/validOrigins';
import { appInitializeHelper } from './app';
import { errorNotSupportedOnPlatform, FrameContexts } from './constants';
import {
ActionInfo,
ErrorCode,
FrameInfo,
ShareDeepLinkParameters,
Expand Down Expand Up @@ -177,6 +178,8 @@ export namespace pages {
export type saveEventType = (evt: pages.config.SaveEvent) => void;
/** Remove event function */
export type removeEventType = (evt: pages.config.RemoveEvent) => void;
/** Response Button event function */
export type responseButtonEventType = (evt: pages.responseButton.ResponseButtonEvent) => void;

/**
* Return focus to the host. Will move focus forward or backward based on where the application container falls in
Expand Down Expand Up @@ -1121,42 +1124,77 @@ export namespace pages {
* @beta
*/
export namespace responseButton {
let responseButtonHandler: undefined | ((evt: ResponseButtonEvent) => void);
/**
* The required information that the app needs to let host apps know.
* The required information that the app needs to let host know about the response button.
*
* @beta
*/
export interface ResponseInfo {
/**
* The unique ID helps the host and apps identify which button it refers to.
* To facilitate easy extension for apps and hosts, we also accept the string type if the response id has not yet been added to the enum.
*
* @beta
*/
responseId: string;
responseId: ResponseId | string;
/**
* Id of File on OneDrive
* The actionInfo that contains the original source for the action object
*
* @beta
*/
oneDriveFileId: string;
actionInfo: ActionInfo;
}

/**
* This is intended to indicate what type of response button the host should display.
*
* @beta
*/
export enum ResponseId {
/** reply button */
reply = 'reply',
}

/**
* Describes the results of the settings.remove event. Includes notifySuccess, and notifyFailure
* to indicate the status of whether the settings.save call succeeded or not and why.
*
* @beta
*/
export interface ResponseButtonEvent {
/**
* The exact message where the action was invoked from
*
* @beta
* Indicates that the response button event has been invoked after user click event.
*/
messageId: string;

notifySuccess(): void;
/**
* The conversation where the action was invoked from
*
* @beta
* Indicates that response button event has been invoked failed and user didn't see expected button click behavior.
* @param reason - Specifies a reason for the failure. If provided, this string is displayed to the user; otherwise a generic error is displayed.
*/
conversionId: string;
notifyFailure(reason?: string): void;
}

/**
* @hidden
* Hide from docs because this function is only used during initialization
*
* Adds register handlers for settings.save and settings.remove upon initialization. Function is called in {@link app.initializeHelper}
* @internal
* Limited to Microsoft-internal use
*/
export function initialize(): void {
registerHandler(
getApiVersionTag(pagesTelemetryVersionNumber, ApiName.Pages_ResponseButton_RegisterResponseButton),
'pages.responseButton.registerResponseButton',
handleResponseButtonEvent,
false,
);
}

/**
* Notify host apps to show the response button.
* @param params - Parameters sent to host apps contain the required information.
* @param params - Parameters {@link ResponseInfo} sent to host apps contain the required information.
* @returns Promise that resolves when the response button has been shown on the host app or reject with an error. Function can also throw a NOT_SUPPORTED_ON_PLATFORM error.
*
* @beta
*/
Expand All @@ -1165,18 +1203,28 @@ export namespace pages {
if (!isSupported()) {
throw errorNotSupportedOnPlatform;
}
if (!params) {
throw { errorCode: ErrorCode.INVALID_ARGUMENTS };
}
return sendAndHandleSdkErrorWithVersion(
checkValidResponseInfo(params);
return sendAndHandleSdkError(
getApiVersionTag(pagesTelemetryVersionNumber, ApiName.Pages_ResponseButton_ShowResponseButton),
'pages.responseButton.showResponseButton',
params,
);
}

/**
* Check if the input contains a valid responseID and originalSource.
*
* @beta
*/
function checkValidResponseInfo(info: ResponseInfo): void {
if (!info || !info.responseId || info.responseId == '' || !info.actionInfo) {
throw { errorCode: ErrorCode.INVALID_ARGUMENTS };
}
}

/**
* Notify host apps to hide the response button
* @returns Promise that resolves when the response button has been shown on the host app or reject with an error. Function can also throw a NOT_SUPPORTED_ON_PLATFORM error.
*
* @beta
*/
Expand All @@ -1185,39 +1233,105 @@ export namespace pages {
if (!isSupported()) {
throw errorNotSupportedOnPlatform;
}
return sendAndHandleSdkErrorWithVersion(
return sendAndHandleSdkError(
getApiVersionTag(pagesTelemetryVersionNumber, ApiName.Pages_ResponseButton_HideResponseButton),
'pages.responseButton.hideResponseButton',
);
}

/**
* Registers a handler for when the button in the host apps is clicked by the user
* @param appEventHandler - The handler to invoke when the user clicks on the button in host apps.
* Register for a handler that’s triggered when the end user clicks on one of the button options in the host.
* The object passed to the handler must be used to notify whether to proceed with the button event.
* Only one handler can be registered at a time. A subsequent registration replaces an existing registration.
* @param handler - The handler to invoke when the user clicks on the button in host apps.
*
* @beta
*/
export function responseButtonEventHandler(appEventHandler: handlerFunctionType): void {
registerHandlerHelperWithVersion(
getApiVersionTag(pagesTelemetryVersionNumber, ApiName.Pages_ResponseButton_ResponseButtonEventHandler),
'pages.responseButton.responseButtonEventHandler',
appEventHandler,
export function registerResponseButtonClickEventHandler(handler: responseButtonEventType): void {
registerHandlerHelper(
getApiVersionTag(
pagesTelemetryVersionNumber,
ApiName.Pages_ResponseButton_RegisterResponseButtonClickEventHandler,
),
'pages.responseButton.registerResponseButtonClickEventHandler',
handler,
[FrameContexts.content],
() => {
if (!isSupported()) {
if (!isNullOrUndefined(handler) && !isSupported()) {
throw errorNotSupportedOnPlatform;
}
},
);
}

/**
* @hidden
* Hide from docs, since this class is not directly used.
*
* @beta
*/
function handleResponseButtonEvent(): void {
const responseButtonEventType = new ResponseButtonEventImpl();
if (responseButtonHandler) {
responseButtonHandler(responseButtonEventType);
} else if (Communication.childWindow) {
sendMessageEventToChild('pages.responseButton', []);
} else {
// If no handler is registered, we assume success.
responseButtonEventType.notifySuccess();
}
}

/**
* @hidden
* Hide from docs, since this class is not directly used.
*
* @beta
*/
class ResponseButtonEventImpl implements ResponseButtonEvent {
public notified = false;

public notifySuccess(): void {
this.ensureNotNotified();
sendMessageToParent(
getApiVersionTag(
pagesTelemetryVersionNumber,
ApiName.Pages_ResponseButton_RegisterResponsButtonClickEvent_Success,
),
'pages.responseButton.registerResponseButtonClickEvent.success',
);
this.notified = true;
}

public notifyFailure(reason?: string): void {
this.ensureNotNotified();
sendMessageToParent(
getApiVersionTag(
pagesTelemetryVersionNumber,
ApiName.Pages_ResponseButton_RegisterResponsButtonClickEvent_Failure,
),
'pages.responseButton.registerResponseButtonClickEvent.failure',
[reason],
);
this.notified = true;
}

private ensureNotNotified(): void {
if (this.notified) {
throw new Error('The responseButtonEventType may only notify success or failure once.');
}
}
}

/**
* @hidden
*
* Checks if the pages.responseButton capability is supported by the host
* @returns boolean to represent whether the pages.responseButton capability is supported
*
* @throws Error if {@linkcode app.initialize} has not successfully completed
*
* @beta
*/
export function isSupported(): boolean {
return ensureInitialized(runtime) && runtime.supports.pages
Expand Down

0 comments on commit 86a313d

Please sign in to comment.