npm package: @sinchlabs/composer
Description: <div align="center">   <img src="https://1vxc0v12qhrm1e72gq1mmxkf-wpengine.netdna-ssl.com/wp-content/uploads/2020/04/cropped-Sinch-PR-1000-300x141-1.png" width="320" alt="Sinch Logo" /> </div>
Latest version: 1.25.0

<div align="center">
  <img src="https://1vxc0v12qhrm1e72gq1mmxkf-wpengine.netdna-ssl.com/wp-content/uploads/2020/04/cropped-Sinch-PR-1000-300x141-1.png" width="320" alt="Sinch Logo" />
</div>

<div align="center">
  <h1>Message Composer</h1>
  <h2>Composing messages compatible with Sinch Conversation API</h2>
</div>

## Getting Started

1. Create an `.env ` file and paste the key to decrypt the package.
   **Example below is just that and real keys needs to be provided by Sinch.**

> `.env`
>
>     DISTCRY = { "composer": "p455w0rd" }

2. Install the Composer package

```bash
$ yarn add @sinchlabs/composer
```

3. Install dependencies:

- @material-ui/core version 4
- @material-ui/icons version 4

## How To Use

Composer provides 5 editors.

- `Inline Editor` allows for visual composing of messages.
- `Code Editor` which makes it possible to create messages by directly editing message as json.
- `Form Editor` which allows creation of message in standard form format.
- `WhatsApp Editor`
  - `WhatsApp Template Editor` allows for composing of messages in WhatsApp templates format.
  - `WhatsApp Samples Editor` allows to add preview values to WhatsApp template samples.
- `KakaoTalk Template Editor` allows for composing of messages in KakaoTalk templates format.

`Inline`, `Code` and `Form` editors has by default a dropdown for selecting type of message.
Default dropdown can be removed using `disableMessageTypeDropdown` props and standalone component, `MessageTypeCard`, or own implimentation can be used instead.

**Inline, Form, Code Editor Props:**

- `message`: The message accepts a message compatible with the conversation API omni format.
- `onChange`: Callback to which is passes the current message in the editor state.
- `onFileUpload` (optional): Function for uploading attachements, needs to return url of attachement.
- `variables` (optional): Quick suggestions of variables to use in message.
- `disabledMessageTypes` (optional): Exclude certain message types.
- `disableMessageTypeDropdown` (optional): Exclude the message type dropdown.
- `theme` (optional): MaterialUI theme object that overrides default values.

**WhatsApp Template Editor Props:**

- `components`: object with WhatsApp Template.
- `onComponentsChange`: Callback to which is passes the current template components state.
- `onError` (optional): Fires when `errors` object changes.
- `theme` (optional): MaterialUI theme object that overrides default values.
- `disabled` (optional): disabling every field in the form.

**WhatsApp Samples Editor Props:**

It has the same props like WhatsApp Template Editor with one extra:

- `onFileUpload`: Function for uploading files, needs to return url of file.

**KakaoTalk Template Editor Props:**

- `template`: object with KakaoTalk Template.
- `onTemplateChange`: Callback to which is passes the current template components state.
- `onError` (optional): Fires when `errors` object changes.
- `theme` (optional): MaterialUI theme object that overrides default values.
- `disabled` (optional): disabling every field in the form.
- `onFileUpload` (optional): Function for uploading files, needs to return url of file.

### Inline Editor

```tsx
import { useState } from 'react';
import { InlineEditor, MessageTypeCard } from '@sinchlabs/composer';

export const Default = () => {
  const [message, setMessage] = useState({ text_message: { text: 'Hej!' } });

  const handleOnChange = (value) => {
    setMessage(value);
  };

  const handleFileUpload = () => {
    // Upload attachement and return url to attachement.
    return new Promise((resolve) => resolve('path-to-attachement'));
  };

  return (
    <>
      <MessageTypeCard message={message} onChange={setMessage} />
      <InlineEditor
        message={message}
        onChange={handleOnChange}
        onFileUpload={handleFileUpload}
        disableMessageTypeDropdown
      />
    </>
  );
};
```

### Code Editor

```tsx
import { useState } from 'react';
import { CodeEditor } from '@sinchlabs/composer';

export const Default = () => {
  const [message, setMessage] = useState({ text_message: { text: 'Hej!' } });

  const handleOnChange = (value) => {
    setMessage(value);
  };

  return <CodeEditor message={message} onChange={handleOnChange} />;
};
```

### Form Editor

```tsx
import { useState } from 'react';
import { CodeEditor } from '@sinchlabs/composer';

export const Default = () => {
  const [message, setMessage] = useState({ text_message: { text: 'Hej!' } });

  const handleOnChange = (value) => {
    setMessage(value);
  };

  const handleFileUpload = () => {
    // Upload attachement and return url to attachement.
    return new Promise((resolve) => resolve('path-to-attachement'));
  };

  return (
    <FormEditor
      message={message}
      onChange={handleOnChange}
      onFileUpload={handleFileUpload}
    />
  );
};
```

### WhatsApp Template Editor

#### Basic example

```tsx
import { WhatsAppTemplateEditor } from '@sinchlabs/composer';

export const Default: FunctionComponent<Props> = () => {
  const [messageComponents, setMessageComponents] = useState(DEFAULT_TEMPLATE);

  const handleChange = (newComponents) => {
    setMessageComponents(newComponents);
  };

  /*
   * Function that as arguments takes errors object and summary count of errors found in the Editor
   */
  const handleError = (errors, errorsCount) => {
    /* ... */
  };

  return (
    <WhatsAppTemplateEditor
      components={messageComponents}
      onComponentsChange={handleChange}
      onError={handleError}
    />
  );
};
```

#### Themed example (in this case override button color to yellow and use custom font)

```tsx
import { WhatsAppTemplateEditor } from '@sinchlabs/composer';

export const Default: FunctionComponent<Props> = () => {
  const [messageComponents, setMessageComponents] = useState(DEFAULT_TEMPLATE);

  const handleChange = (newComponents) => {
    setMessageComponents(newComponents);
  };

  /*
   * Function that as arguments takes errors object and summary count of errors found in the Editor
   */
  const handleError = (errors, errorsCount) => {
    /* ... */
  };

  /*
   * You can extend default fonts by passing them to the theme.
   * Be aware that Message Composer does not provide font files, you can just change default `CSS` `font-family`
   * property and provide all necessary resources like font files or `@font-family`
   */
  const customFonts = 'AtlasGroteskWeb, Helvetica';

  const customTheme = {
    typography: {
      fontFamily: customFonts,
    },
    overrides: {
      MuiButton: {
        containedPrimary: {
          color: '#000000',
          backgroundColor: '#ffc658',

          '&:hover': {
            backgroundColor: '#ffc658',
          },
        },
      },
    },
  };

  return (
    <WhatsAppTemplateEditor
      components={messageComponents}
      onComponentsChange={handleChange}
      theme={customTheme}
      onError={handleError}
    />
  );
};
```

### WhatsApp Template Components

```tsx
import {
  WhatsAppTemplateBodySolo,
  WhatsAppTemplateButtonsSolo,
  WhatsAppTemplateFooterSolo,
  WhatsAppTemplateHeaderSolo,
} from '@sinchlabs/composer';

export const Header: FunctionComponent<Props> = ({ disabled = false }) => {
  const [format, setFormat] = useState<WAMessageTemplateComponentFormat>();
  const [text, setText] = useState<string>();

  const handleChange = (payload: {
    text?: WAMessageTemplateComponent['text'];
    format?: WAMessageTemplateComponent['format'];
  }) => {
    typeof payload.format !== 'undefined' && setFormat(payload.format);
    typeof payload.text !== 'undefined' && setText(payload.text);
  };

  return (
    <WhatsAppTemplateHeaderSolo
      disabled={disabled}
      format={format}
      onChange={handleChange}
      text={text}
    />
  );
};

export const Body: FunctionComponent<Props> = ({ disabled = false }) => {
  const [bodySamples, setBodySamples] = useState<string[]>();
  const [text, setText] = useState<string>();

  const handleChange = (payload: {
    text?: WAMessageTemplateComponent['text'];
    bodySamples?: WAMessageTemplateComponent['bodySamples'];
  }) => {
    typeof payload.bodySamples !== 'undefined' &&
      setBodySamples(payload.bodySamples);
    typeof payload.text !== 'undefined' && setText(payload.text);
  };

  return (
    <WhatsAppTemplateBodySolo
      bodySamples={bodySamples}
      disabled={disabled}
      onChange={handleChange}
      text={text}
    />
  );
};

export const Footer: FunctionComponent<Props> = ({ disabled = false }) => {
  const [text, setText] = useState<string>();

  const handleChange = (payload: {
    text?: WAMessageTemplateComponent['text'];
  }) => {
    typeof payload.text !== 'undefined' && setText(payload.text);
  };

  return (
    <WhatsAppTemplateFooterSolo
      disabled={disabled}
      onChange={handleChange}
      text={text}
    />
  );
};

export const Buttons: FunctionComponent<Props> = ({ disabled = false }) => {
  const [buttons, setButtons] = useState<WAMessageTemplateComponent['buttons']>(
    []
  );

  const handleChange = (payload: {
    buttons?: WAMessageTemplateComponent['buttons'];
  }) => {
    typeof payload.buttons !== 'undefined' && setButtons(payload.buttons);
  };

  return (
    <WhatsAppTemplateButtonsSolo
      buttons={buttons}
      disabled={disabled}
      onChange={handleChange}
    />
  );
};
```

### KakaoTalk Template Editor

Optionally you can wrap `<KakaoTalkTemplateEditor />` with `<KakaoTalkTemplateProvider />` and consume it with `useKakaoTalkTemplate()` hook which returns
object with `validateTemplate()` function that allows to trigger manual template validation.

#### Basic example

```tsx
import { KakaoTalkTemplateEditor } from '@sinchlabs/composer';

export const Default: FunctionComponent<Props> = () => {
  const [template, setTemplate] = useState<KakaoTalkTemplate>();

  const handleChange = (newTemplate: KakaoTalkTemplate) => {
    setTemplate(newTemplate);
  };

  /*
   * Function that as arguments takes errors object and summary count of errors found in the Editor
   */
  const handleError = (errors, errorsCount) => {
    /* ... */
  };

  return (
    <KakaoTalkTemplateEditor
      onError={handleError}
      onTemplateChange={handleChange}
      template={template}
    />
  );
};
```