@textback/notification-widget
Version:
TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
570 lines (428 loc) • 27.8 kB
Markdown
# Установка виджета на сайт
Для установки виджета подписки на сайт, сначала добавьте к вашей странице:
```
<script src="//cdn.jsdelivr.net/npm/@textback/notification-widget@latest/build/index.js"></script>
```
**Внимание** Виджет больше не требует подключения дополнительного скрипта с полифиллом.
Для корректного отображение виджета на адаптивных сайтах на мобильных требуется добавить следующий мета-тег в секцию `<head>`:
```html
<meta name="viewport" content="width=device-width, initial-scale=1.0>
```
Затем вы можете вставить один или несколько виджетов:
```
<tb-notification-widget widget-id="YOUR_WIDGET_ID"></tb-notification-widget>
```
с необходимыми widget-id. YOUR_WIDGET_ID указан в консоли администратора Textback.
Если вы планируете использовать виджет в режиме "попап", то встраивайте виджет непосредственно внутри тега body, лучше сразу перед `</body>` шаблона страницы, на которую добавляется виджет.
Если виджет будет использоваться в режиме "инлайн", то добавте виджет в том месте шаблона, в котором он должен отобразиться.
Дополнительные параметры можно передать через data-атрибуты:
```
<tb-notification-widget
widget-id="YOUR_WIDGET_ID"
data-user-id="USER_ID"
data-order-id="ORDER_ID">
</tb-notification-widget>
```
Чтобы передать secureContext, нужно использовать атрибут `secure-context-token`:
```html
<tb-notification-widget
widget-id="YOUR_WIDGET_ID"
secure-context-token="YOUR_TOKEN"
</tb-notification-widget>
```
## Динамическая инициализация виджета
Если вам необходимо динамически проинициализировать виджет, например во всплывающем окне или при разработке Single Page Application
вы можете использовать класс, который доступен стразу после загрузки скрипта виджета
```
var widgetContainer = document.querySelector('tb-notification-widget');
var options = {
widgetId: "YOUR_WIDGET_ID",
element: widgetContainer,
data: {userId: "USER_ID", orderId: "ORDER_ID"}
};
new TextBack.NotificationWidget(options)
```
где widgetContainer - корневой HTML элемент, в котором необходимо отобразить виджет, data - дополнительные параметры.
Чтобы изменить параметры после инициализации виджета повторно проинициализируйте виджет на элементе с новыми параметрами:
```
options.data.orderId = "NEW_ORDER_ID";
new TextBack.NotificationWidget(options)
```
### Мгновенно отобразить popup-виджет
По умолчанию при инициализации виджета используются параметры тайминга из настроек - через сколько секунд отобразить виджет, сколько раз за сессию и т.д.
Если требуется отобразить виджет сразу, то можно воспользоваться разработанным для этого API.
Сначала нужно добавить виджет на страницу, указав атрибут ```only-manual```
```html
<tb-notification-widget widget-id="YOUR_WIDGET_ID" only-manual></tb-notification-widget>
```
Добавленный таким образом виджет будет скрыт и не будет показан автоматически.
Чтобы теперь отобразить виджет, нужно выполнить следующий код:
```javascript
TextBack.NotificationWidget.getWidget('YOUR_WIDGET_ID').then(function(widget) {
widget.show();
});
```
Пример отображения виджета при нажатии на кнопку:
```javascript
document.querySelector('#YOUR_BUTTON_ID').addEventListener('click', function() {
TextBack.NotificationWidget.getWidget('YOUR_WIDGET_ID').then(function(widget) {
widget.show();
});
});
```
### Предпросмотр виджета
Релизован механизм, позволяющий задать JSON виджета динамически, не
подгружая его по ID с нашего **API**. Для того, чтобы подписки работали
в предпросомтре, необходимо, чтобы виджет был сохранен в БАЗУ
и имел присвоенный ID.
---
Этот механизм внедрян для добавления функционала пред-просмотра виджета
в нашем редакторе, но ижет быть использован сторонними разработчикми,
для динамической генерации сообщения приветствия, однака надо
иметь в виду, что **ЭТО апи не финальное, и может изменится в любой момент**,
используйте на свой страх и риск.
---
Использование:
```javascript
//получаем копию настроек виджета
var data = angular.copy(notificationWidgetJSON);
//override settings for preview purpose
//переопределяем некторыесвойства, сохраненные на сервере
//отображатся в виджете будет нвое значение.
data.displayOptions.onLeave = null;
data.displayOptions.timeoutDelay = 0;
data.displayOptions.onTimeout = "yes";
//к примеру vkApiId привязан к домену, на котром отображается виджет
data.vkApiId=TextBack.configuration.get('vkAppId');
//динамически создаем контейнер для виджета
var widgetContainerJq = $('<div></div>');
$('body').append(widgetContainerJq);
var widgetContainer = widgetContainerJq[0];
var options = {
apiPath: 'https://api.textback.io/api', //необязатльный параметр
element: widgetContainer,
widgetConfig: data
};
new TextBack.NotificationWidget(options)
```
# Локализация
Все поддерживаемые языки находятся в переменной `TextBack.NotificationWidget.locales`. Перевод затрагивает только статичные элементы виджета, значения которых не задаются в настройках виджета.
Чтобы применить нужный язык к виджету, передайте соответсвующий параметр `lang` в тег виджета:
```
<tb-notification-widget widget-id="YOUR_WIDGET_ID" lang="ru">
</tb-notification-widget>
```
или
```
var widgetContainer = document.querySelector('tb-notification-widget');
var options = {
widgetId: "YOUR_WIDGET_ID",
element: widgetContainer,
lang: 'ru'
};
new TextBack.NotificationWidget(options)
```
Чтобы изменить существующий перевод, передайте новое значение для нужной переменной в **TextBack.NotificationWidget.locales**:
```
<script>TextBack.NotificationWidget.locales.ru.whatsappb = 'Узнать больше'</script>
```
Вы можете создать новый перевод для всех элементов виджета (это касается как виджета подписок, так и WhatsApp Hunter):
1. В личном кабинете установите значение языка по умолчанию, чтобы аттрибут lang не передавался с нашего сервера;
2. В теге виджета на странице установите аттрибут lang со значением языка (это будет название новой локализации - locales);
3. Передайте значения для всех переменных в массиве после кода виджета. Для незаданных переменных будет использоваться язык по умолчанию - английский.
**Важно!** И для виджета подписок, и для WhatsApp Hunter перевод берется из TextBack.NotificationWidget.locales. Если на странице используются оба виджета и обоим нужно задать полную кастомную локализацию в рамках одного locales, значения переменных нужно объявлять в одном массиве. Если объявить переменные по отдельности, то использоваться будет последняя переменная - неполная.
Если для виджетов заданы разные lang, то объявите переменные для каждого виджета в отдельном массиве.
**Пример перевода виджета подписок на французский**
```
<script>
TextBack.NotificationWidget.locales.fr = {
facebook: "Facebook",
facebookExtended: "Abonnez-vous à Facebook",
telegram: "Telegram",
telegramExtended: "Abonnez-vous à Telegram",
viber: "Viber",
viberExtended: "Subscribe to Viber",
vkontakte: "VK",
vkontakteExtended: "Abonnez-vous à Viber",
whatsapp: "WhatsApp",
whatsappExtended: "Abonnez-vous à WhatsApp",
whatsappb: "WhatsApp",
whatsappbExtended: "Abonnez-vous à WhatsApp"
}
</script>
```
### Список всех переменных
Виджет подписок (пример для английского языка)
```
facebook: "Facebook", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
facebookExtended: "Subscribe to Facebook", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
telegram: "Telegram", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
telegramExtended: "Subscribe to Telegram", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
viber: "Viber", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
viberExtended: "Subscribe to Viber", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
vkontakte: "VK", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" и "попап".
vkontakteExtended: "Subscribe to VK", //если выбран лендинг ВК и вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
whatsapp: "WhatsApp", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
whatsappExtended: "Subscribe to WhatsApp", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
whatsappb: "WhatsApp", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
whatsappbExtended: "Subscribe to WhatsApp", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
//NB: если не использовать лендинг ВК, то при выборе прямоугольных кнопок или попапа используется нативная кнопка, перевод которой пока не реализован
```
WhatsApp Hunter (пример для английского языка)
```
getAnswer: "Get an answer in ", //согласие на получение сообщений
enterNumber: "Enter your phone number", //placeholder в инпуте
sendMessage: "We'll send a message in 3-2-1 sec", //В процессе отправки сообщения
answerSuccessful: "We've answered you in WhatsApp. <br> Check your phone", //После успешной отправки
numberNotFound: "There is no such user in WhatsApp. <br> Are you from Mars?🤔", //Ошибка, формат номера некорректный
tryAgain: "Try another number", //Повтор ввода, если номер не найден
somethingWentWrong: "Oops! Something went wrong", //на 500 при отправке сообщения (например, канал не подключен)
errorTryAgain: "Try again", //Повтор ввода при ошибки 500
```
# TextBack Widget SDK
TextBack Widget SDK предоставляет набор функций для управления подпиской.
SDK входит в состав виджета, но может быть подключен отдельно(см. ниже).
**Преимущества SDK, в сравнении с виджетом подписок:**
- SDK позволяет полностью настраивать внешний вид вашего UI, предоставляя только функции подписки пользователей.
- Файл с SDK имеет меньший размер, чем файл с виджетом подписок.
## Как использовать SDK
### Подключение
Добавить в код страницы
```
<script src="//cdn.jsdelivr.net/npm/@textback/notification-widget@latest/build/sdk.js"></script>
```
После этого на странице будет доступен объект ```TextBack.SDK```
### Использование
Приведенный ниже код будет подписывать пользователя в Telegram при клике на ссылку с `id="MY_TG_BUTTON"`:
```javascript
var config = {
widgetId: 'YOUR_WIDGET_ID'
};
TextBack.SDK.initWidget(config).then(
function(widget) {
document.getElementById('MY_TG_BUTTON').addEventListener('click', function(event) {
event.preventDefault();
widget.subscribe('tg');
})
}
);
```
**ВАЖНО!** Элемент с `id="MY_TG_BUTTON"` должен присутствовать на странице во время исполнения скрипта.
Более подробное описание функции `initWidget()`, состава полей объекта конфигурации и других функций SDK смотрите ниже.
## SDK API
**Примечание.** В документации по SDK под "виджетом" подразумевается не "виджет подписок", упоминавшийся ранее, а объект, который содержит настройки и методы виджета. Таким образом "**инициализация виджета**" - это не отрисовка виджета на странице, а **загрузка настроек с сервера и подготовка к работе**.
Иными словами: "виджет" = "объект в приложении", "виджет подписок" = "объект в JavaScript" + "UI".
### Глобальное пространство имен
SDK и виджет подписок доступны как свойства `SDK` и `NotificationWidget` глобального объекта `TextBack` соответственно.
Для своей работы SDK использует полифиллы для метода `window.fetch()` и объекта ES6 `Promise`.
Также, для работы с каналами VKontakte, используется VK JS API, представленное глобальным объектом VK.
### TextBack.SDK API
Объект `TextBack.SDK` предоставляет API для инициализации виджетов и работы с ними.
#### Метод `initWidget(config)`
Инициализирует новый виджет по заданному объекту конфигурации.
**Принимает**
Объект `config` со следующими полями:
- widgetId - (обязательный) идентификатор виджета. Если виджет с таким идентификатором уже есть, то виджет будет перезагружен;
- secureContextToken - строка;
- insecureContext - объект;
- apiPath - адрес сервера;
- overrideConfig - объект с настройками виджета. Если задан, то настройки виджета не будут загружаться с сервера, и для инициализации виджета будет использован данный объект;
- customData - произвольные пользовательские данные.
**Возвращает** Promise, который будет fulfilled объектом виджета.
##### Пример использования
```javascript
var config = {
widgetId: 'YOUR_WIDGET_ID',
insecureContext: {
data: 'data'
}
};
TextBack.SDK.initWidget(config).then(
function(widget) {
console.log('Widget has been initialized.');
}
);
```
#### Метод `getWidget(widgetId)`
**Принимает** `widgetId` - идентификатор виджета.
**Возвращает** Promise, возвращаемый функцией `initWidget()`, соответствующий переданному идентификатору.
##### Пример использования
```javascript
TextBack.SDK.getWidget('YOUR_WIDGET_ID').then(
function(widget) {
console.log('Widget has id = ' + widget.id);
}
)
```
#### Метод `on(eventName, callback)`
Вешает обработчик `callback` на событие `eventName`. При наступлении события в `callback` первым аргументом будет передан объект события. Описание полей объекта события для различных событий см. ниже.
**Поддерживаемые события**
- 'widget.init' - успеная инициализация виджета. поля события:
- widgetId - идентификатор виджета
- 'subscribe.start' - вызвана функция подписки на канал. поля события:
- widgetId - идентификатор виджета
- channel - объект канала, на который осуществляется подписка
**Возвращает**
Функцию отписки обработчика `callback` от события.
##### Пример использования
При инициализации первого виджета вывести в консоль сообщение.
```javascript
const config = {
widgetId: 'YOUR_WIDGET_ID'
};
TextBack.SDK.initWidget(config);
const off = TextBack.SDK.on('widget.init', function(event) {
console.log('First widget with id = ' + event.widgetId + ' has been initilized');
off();
})
```
#### Метод `deeplinkUpdater(String)`
#### deeplinkUpdater :: String -> Promise -> Function
Ф-ция getDeeplinkUpdater() принимает идентефикатор виджета и возвращает Promise, содержащий ф-цию, позволяющую обновлять данные в insecureContext с помощью PATCH запроса.
##### Пример использования
Данный пример отправит в insecureContext данные на событие подписки.
```javascript
TextBack.SDK.getDeeplinkUpdater('myWidgetId').then(f => {
let data = {
foo: 'foo',
bar: 'bar'
};
TextBack.SDK.on('subscribe.start', () => f(data));
});
```
### Widget API
Объект Widget предоставляет функции для работы с конкретным виджетом.
#### Метод `subscribe(channelType)`
Инициализирует подписку пользователя на канал.
**Принимает** channelType - тип канала, на который следует осуществить подписку. Поддерживаются следующие коды каналов:
- `'facebook'` - Facebook
- `'tg'` - Telegram
- `'viber'` - Viber
- `'vk'` - Vkontakte
- `'whatsapp'` - WhatsApp
- `'whatsappb'` - WhatsApp Business API
- `'skype'` - Skype
**Внимание!**
- Мы **настоятельно рекомендуем** вызывать эту функцию непосредственно из обработчика клика на кнопку/ссылку, во избежание блокирования функции браузером.
##### Пример использования
```javascript
var config = {
widgetId: 'YOUR_WIDGET_ID'
};
TextBack.SDK.initWidget(config).then(
function(widget) {
document.getElementById('MY_TG_BUTTON').addEventListener('click', function(event) {
event.preventDefault();
widget.subscribe('tg');
});
document.getElementById('MY_VK_BUTTON').addEventListener('click', function(event) {
event.preventDefault();
widget.subscribe('vk');
})
}
);
```
#### Метод `getConfig()`
**Возвращает** загруженный с сервера объект конфигурации виджета
#### Метод `getChannels()`
**Возвращает** объекты каналов виджета(см. `Channel` ниже)
#### Метод `getEnabledChannels()`
**Возвращает** объекты каналов виджета, которые сейчас активны и инициализированны без ошибок.
##### Пример использования
```javascript
var config = {
widgetId: 'YOUR_WIDGET_ID'
};
TextBack.SDK.initWidget(config).then(
function(widget) {
console.log('Total channels: ' + widget.getChannels().length);
console.log('Enabled channels: ' + widget.getEnabledChannels().length);
}
);
```
### Channel API
Объекты `Channel` предоставляют API конкретных каналов в виджете. Каждый канал реализует свою собственную функцию подписки пользователя.
#### Свойство `hasError`
Устанавливается в значение `true`, если при инициализации канала произошла ошибка (например, если не удалось загрузить внешнюю библиотеку для канала VKontakte). Информация об ошибке будет выведена в консоль.
Каналы, для которых `hasError == true` не вовзращаются при вызове метода `getEnabledChannels()` у объекта виджета.
#### Метод `subscribe()`
Инициализирует процесс подписки пользователя на канал.
##### Пример использования
```javascript
var config = {
widgetId: 'YOUR_WIDGET_ID'
};
TextBack.SDK.initWidget(config).then(
function(widget) {
document.getElementById('MY_BUTTON').addEventListener('click', function(event) {
event.preventDefault();
const channels = widget.getEnabledChannels();
if (channels.length > 0) {
channels[0].subscribe();
console.log('User has been subscribed on ' + channels[0].channel + ' channel');
} else {
console.log('No channels available for subscription');
}
});
}
);
```
## Пример
Страница с двумя ссылками, подписывающими в Telegram и Vkontakte.
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TextBack SDK Example</title>
</head>
<body>
<h1>TextBack SDK</h1>
<a href="" id="sign_tg">Telegram</a>
<a href="" id="sign_vk">Vkontakte</a>
<script src="//cdn.jsdelivr.net/npm/@textback/notification-widget@latest/build/sdk.js"></script>
<script type="text/javascript">
var config = {
widgetId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
};
var off = TextBack.SDK.on('widget.init', function(event) {
console.log('First widget has been initialized. ' + event.widgetId);
off();
});
TextBack.SDK.initWidget(config).then(function(widget) {
document.getElementById('sign_tg').addEventListener('click', function(event) {
event.preventDefault();
widget.subscribe('tg');
});
document.getElementById('sign_vk').addEventListener('click', function(event) {
event.preventDefault();
widget.subscribe('vk');
});
});
</script>
</body>
</html>
```
# Разработка
В терминале перейдите в директорию исходного кода виджета и выполните команды, использую версию ноды v.12:
```
npm run dev
PORT=8080 npm start
```
После этого тестовый стенд будет доступен локально по адресу http://localhost:8080/examples
Если переменная PORT не указана, по умолчанию будет использован порт 3000
После любых изменений кода обновите страницу браузера
Для проверки codestyle выполните
```
npm run test:lint
```
# Тестирование
Для запуска тестов выполните
```
npm run build
npm run test:all
```
Тесты будут запущенны во всех браузерах, установленных на локальной машине.
Тестирование осуществляется с помощью https://github.com/DevExpress/testcafe
Все тесты запускаются из директории `./tests`