Skip to content

Commit

Permalink
Merge pull request #311 from Secreto31126/interactive-breaking-update
Browse files Browse the repository at this point in the history
Interactive breaking update
  • Loading branch information
Secreto31126 authored Feb 29, 2024
2 parents 9b29e29 + 3549a81 commit b50390a
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 51 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
package-lock.json
.all-contributorsrc
playground.js
1 change: 1 addition & 0 deletions src/messages/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { AtLeastOne } from "../utils";
/**
* TS knowledge intensifies
* @internal
* @deprecated - Unused with the release of ActionProductList
*/
export function isProductSections(obj: unknown[]): obj is ProductSection[] {
return obj[0] instanceof ProductSection;
Expand Down
236 changes: 185 additions & 51 deletions src/messages/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { AtLeastOne } from "../utils";

import type { Document, Image, Video } from "./media";

import { Product, ProductSection, isProductSections } from "./globals.js";
import { Product, ProductSection } from "./globals.js";

/**
* Interactive API object
Expand All @@ -17,19 +17,19 @@ import { Product, ProductSection, isProductSections } from "./globals.js";
*/
export class Interactive extends ClientMessage {
/**
* The action component of the interactive message
* The action for the interactive message
*/
readonly action: InteractiveAction;
/**
* The body component of the interactive message
* The body for the interactive message
*/
readonly body?: Body;
/**
* The header component of the interactive message
* The header for the interactive message
*/
readonly header?: Header;
/**
* The footer component of the interactive message
* The footer for the interactive message
*/
readonly footer?: Footer;

Expand All @@ -46,17 +46,124 @@ export class Interactive extends ClientMessage {
return "interactive";
}

/**
* Creates an Interactive Reply Buttons object for the API
*
* @param action - The action for the interactive message
* @param body - The body for the interactive message
* @param header - The header for the interactive message, it may be undefined if not needed
* @param footer - The footer for the interactive message, it may be undefined if not needed
*/
constructor(
action: ActionButtons,
body: Body,
header?: Header,
footer?: Footer
);
/**
* Creates an Interactive List object for the API
*
* @param action - The action for the interactive message
* @param body - The body for the interactive message
* @param header - The header of type text for the interactive message, it may be undefined if not needed
* @param footer - The footer for the interactive message, it may be undefined if not needed
* @throws If a header is provided and it's not of type "text"
*/
constructor(
action: ActionList,
body: Body,
header?: Header,
footer?: Footer
);
/**
* Creates an Interactive Catalog object for the API
*
* @param action - The action for the interactive message
* @param body - The body for the interactive message
* @param header - Undefined
* @param footer - The footer for the interactive message, it may be undefined if not needed
*/
constructor(
action: ActionCatalog,
body: Body,
header?: undefined,
footer?: Footer
);
/**
* Creates an Interactive Single Product object for the API
*
* @param action - The action for the interactive message
* @param body - The body for the interactive message
* @param header - Undefined
* @param footer - The footer for the interactive message, it may be undefined if not needed
*/
constructor(
action: ActionProduct,
body?: Body,
header?: undefined,
footer?: Footer
);
/**
* Creates an Interactive Multi Product object for the API
*
* @param action - The action for the interactive message
* @param body - The body for the interactive message
* @param header - The header of type text for the interactive message
* @param footer - The footer for the interactive message, it may be undefined if not needed
* @throws If header is not of type "text"
*/
constructor(
action: ActionProductList,
body: Body,
header: Header,
footer?: Footer
);
/**
* Creates an Interactive CTA object for the API
*
* @param action - The action for the interactive message
* @param body - The body for the interactive message
* @param header - The header of type text for the interactive message, it may be undefined if not needed
* @param footer - The footer for the interactive message, it may be undefined if not needed
* @throws If a header is provided and it's not of type "text"
*/
constructor(
action: ActionCTA,
body: Body,
header?: Header,
footer?: Footer
);
/**
* Creates an Interactive Flow object for the API
*
* @param action - The action for the interactive message
* @param body - The body for the interactive message
* @param header - The header of type text for the interactive message, it may be undefined if not needed
* @param footer - The footer for the interactive message, it may be undefined if not needed
* @throws If a header is provided and it's not of type "text"
*/
constructor(
action: ActionFlow,
body: Body,
header?: Header,
footer?: Footer
);
/**
* Creates an Interactive Flow object for the API
*
* @param action - The action for the interactive message
* @param body - The body of the interactive message
*/
constructor(action: ActionLocation, body: Body);

/**
* Create an Interactive object for the API
*
* @param action - The action component of the interactive message
* @param body - The body component of the interactive message, it may be undefined if not needed.
* @param header - The header component of the interactive message, it may be undefined if not needed.
* @param footer - The footer component of the interactive message, it may be undefined if not needed.
* @throws If body is not provided, unless action is an {@link ActionProduct} with a single product
* @throws If header is provided for an {@link ActionProduct} with a single product
* @throws If header of type text is not provided for an {@link ActionProduct} with a product list
* @throws If header is not of type text, unless action is an {@link ActionButtons}
* @param action - The action for the interactive message
* @param body - The body for the interactive message, it may be undefined if not needed.
* @param header - The header for the interactive message, it may be undefined if not needed.
* @param footer - The footer for the interactive message, it may be undefined if not needed.
* @throws If a header is provided for an {@link ActionList}, {@link ActionProductList}, {@link ActionCTA} or {@link ActionFlow} and it's not of type "text"
*/
constructor(
action: InteractiveAction,
Expand All @@ -66,18 +173,22 @@ export class Interactive extends ClientMessage {
) {
super();

if (action._type !== "product" && !body)
throw new Error("Interactive must have a body component");
if (action._type === "product" && header)
throw new Error(
"Interactive must not have a header component if action is a single product"
);
if (action._type === "product_list" && header?.type !== "text")
const require_text_header: InteractiveAction["_type"][] = [
"list",
"product_list",
"cta_url",
"flow"
];

if (
header &&
require_text_header.includes(action._type) &&
header.type !== "text"
) {
throw new Error(
"Interactive must have a text header component if action is a product list"
`Header of type text is required for ${action._type} action`
);
if (header && action._type !== "button" && header?.type !== "text")
throw new Error("Interactive header must be of type text");
}

this.type = action._type;

Expand Down Expand Up @@ -469,56 +580,79 @@ export class ActionProduct implements InteractiveAction {
*/
readonly catalog_id: string;
/**
* The product to be added to the catalog
* The product to show in the message
*/
readonly product_retailer_id: string;

/**
* @override
* @internal
*/
get _type(): "product" {
return "product";
}

/**
* Builds a Single Product component for an Interactive message
*
* @param catalog_id - The catalog id
* @param product - The product to show in the message
*/
readonly product_retailer_id?: string;
constructor(catalog_id: string, product: Product) {
this.catalog_id = catalog_id;
this.product_retailer_id = product.product_retailer_id;
}
}

/**
* Action API object
*
* @group Interactive
*/
export class ActionProductList
extends ClientLimitedMessageComponent<ProductSection, 10>
implements InteractiveAction
{
/**
* The id of the catalog from where to get the products
*/
readonly catalog_id: string;
/**
* The section to be added to the catalog
* The sections to show in the message
*/
readonly sections?: ProductSection[];
readonly sections: ProductSection[];

/**
* @override
* @internal
*/
get _type(): "product" | "product_list" {
return this.product_retailer_id ? "product" : "product_list";
get _type(): "product_list" {
return "product_list";
}

/**
* Builds a Multi or Single Product component for an Interactive message
* Builds a Multi Product component for an Interactive message
*
* @param catalog_id - The catalog id
* @param products - The products to add to the catalog. It can be a _single_ Product object, or a list of ProductSections.
* @throws If products is a product list and more than 10 sections are provided
* @throws If products is a product list with more than 1 section and at least one section is missing a title
* @param sections - The product sections to show in the message
* @throws If more than 10 product sections are provided
* @throws If more than 1 product section is provided and at least one section is missing a title
*/
constructor(
catalog_id: string,
...products: [Product] | AtLeastOne<ProductSection>
) {
const is_sections = isProductSections(products);
constructor(catalog_id: string, ...sections: AtLeastOne<ProductSection>) {
super("ActionProductList", "sections", sections, 10);

if (is_sections) {
if (products.length > 1) {
if (products.length > 10)
if (sections.length > 1) {
for (const obj of sections) {
if (!obj.title) {
throw new Error(
"Catalog must have between 1 and 10 product sections"
"All sections must have a title if more than 1 section is provided"
);
for (const obj of products) {
if (!obj.title) {
throw new Error(
"All sections must have a title if more than 1 section is provided"
);
}
}
}
}

this.catalog_id = catalog_id;

if (is_sections) this.sections = products;
else this.product_retailer_id = products[0].product_retailer_id;
this.sections = sections;
}
}

Expand Down

0 comments on commit b50390a

Please sign in to comment.