@viibe/impulse-integration-js 2.1.0 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +207 -0
- package/dist/index.js +1 -1
- package/package.json +4 -1
- package/src/index.js +134 -19
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Viibe SDK
|
|
2
|
+
|
|
3
|
+
Viibe SDK allows to display a call iframe directly within your host application.
|
|
4
|
+
|
|
5
|
+
The call iframe allows to
|
|
6
|
+
|
|
7
|
+
- initiate a call
|
|
8
|
+
- schedule a call
|
|
9
|
+
- display past sessions
|
|
10
|
+
|
|
11
|
+
### Constructor
|
|
12
|
+
|
|
13
|
+
`const viibe = new Viibe({host, source})`
|
|
14
|
+
|
|
15
|
+
**Options**
|
|
16
|
+
|
|
17
|
+
`host` **(string, mandatory)**
|
|
18
|
+
|
|
19
|
+
The ViiBE subdomain you want to display.
|
|
20
|
+
|
|
21
|
+
i.e : [`example.viibe.pro`](http://example.viibe.pro)
|
|
22
|
+
|
|
23
|
+
`source` **(string, mandatory)**
|
|
24
|
+
|
|
25
|
+
The source where your want to integrate ViiBE.
|
|
26
|
+
|
|
27
|
+
The source will be added to the created ticket for reference.
|
|
28
|
+
|
|
29
|
+
i.e: `example`
|
|
30
|
+
|
|
31
|
+
### Create a Call
|
|
32
|
+
|
|
33
|
+
⚠️ You will need a ViiBE token to display our iframe without login step.
|
|
34
|
+
|
|
35
|
+
`viibe.createCallIframe(options): HTMLElement`
|
|
36
|
+
|
|
37
|
+
This method will create an iframe element which will display a initialization call page of your viibe subdomain. The iframe should be appended to the host application's DOM.
|
|
38
|
+
|
|
39
|
+
`viibe.createCallURL(options): string`
|
|
40
|
+
|
|
41
|
+
The method will generate the URL to the call page (used by `createCallUrl` ). The host application can use this URL to display the call frame in a popin or modal instead of an iframe.
|
|
42
|
+
|
|
43
|
+
#### Options
|
|
44
|
+
|
|
45
|
+
Both above methods support the same options:
|
|
46
|
+
|
|
47
|
+
`links[]` **(array, optional)**
|
|
48
|
+
|
|
49
|
+
The list of contacts to add to the call. The contacts are only prefilled and can be removed by the user before initiating the call. Other contacts can be added by the user via an input.
|
|
50
|
+
|
|
51
|
+
Each item is an object with the following format:
|
|
52
|
+
|
|
53
|
+
`category` **(string, mandatory)**
|
|
54
|
+
|
|
55
|
+
The type of contact (`email`, `expertise`, or `phone`)
|
|
56
|
+
|
|
57
|
+
`value` **(string, mandatory)**
|
|
58
|
+
|
|
59
|
+
The contact itself;
|
|
60
|
+
|
|
61
|
+
- for `email` category, should be a valid email
|
|
62
|
+
- for `phone` category, should be a full phone number (including national prefix)
|
|
63
|
+
- for `expertise` category, should be an `id_expertise`
|
|
64
|
+
|
|
65
|
+
`ticket`
|
|
66
|
+
|
|
67
|
+
Represents the host application entity the call will be attached to. Viibe calls should typically be attached to a ticket, conversation or issue within the host application
|
|
68
|
+
|
|
69
|
+
`integration`
|
|
70
|
+
|
|
71
|
+
`id` **(string, mandatory)**
|
|
72
|
+
|
|
73
|
+
The external ID corresponding to ticket
|
|
74
|
+
|
|
75
|
+
`title` **(string, optional)**
|
|
76
|
+
|
|
77
|
+
The ticket title (will overwrite any existing ticket title)
|
|
78
|
+
|
|
79
|
+
`token` **(string, optional)**
|
|
80
|
+
|
|
81
|
+
A valid Viibe JWT token. If none is provided or the token is invalid, a `authentication:error` event will be triggered, and a "Login" button will be displayed within the iframe.
|
|
82
|
+
|
|
83
|
+
`locale` **(string, optional)**
|
|
84
|
+
|
|
85
|
+
An **ISO 639-1** language code.
|
|
86
|
+
|
|
87
|
+
`components` **(array, optional)**
|
|
88
|
+
|
|
89
|
+
A list of visible components among `call`, `schedule` and `session` (example: `['call','schedule']`)
|
|
90
|
+
|
|
91
|
+
`o2m` **(boolean, optional, default = false)**
|
|
92
|
+
|
|
93
|
+
Whether or not the user can call an expertise (i.e. a group of experts)
|
|
94
|
+
|
|
95
|
+
`clipboard` **(boolean, optional, default = false)**
|
|
96
|
+
|
|
97
|
+
If true, the user won't be able to edit the list of callees (links)
|
|
98
|
+
|
|
99
|
+
### Authentication
|
|
100
|
+
|
|
101
|
+
While the iframe should contain a "Login" button if you don't pass a token or your token is invalid, the Viibe SDK allows to open an authentication popup to obtain a Viibe JWT token to be passed to the call iframe:
|
|
102
|
+
|
|
103
|
+
`viibe.authenticate(provider): Promise<string>`
|
|
104
|
+
|
|
105
|
+
The `provider` param depends on the third-party authentication methods setup for your Viibe domain.
|
|
106
|
+
|
|
107
|
+
### Events
|
|
108
|
+
|
|
109
|
+
The host application can register event listeners to react to events occuring in the call iframe
|
|
110
|
+
|
|
111
|
+
`viibe.on(event, handler)`
|
|
112
|
+
|
|
113
|
+
`handler` will receive the event emitted by the call iframe
|
|
114
|
+
|
|
115
|
+
**Supported events**
|
|
116
|
+
|
|
117
|
+
- event `resize`
|
|
118
|
+
- handler payload `{height: number}`
|
|
119
|
+
|
|
120
|
+
Triggered when the height of the iframe content changes. The host app should likely resize the iframe to the appropriate size
|
|
121
|
+
|
|
122
|
+
- event `click:session`
|
|
123
|
+
- handler payload `{uuid: string, slug: string}`
|
|
124
|
+
|
|
125
|
+
Triggered when the user clicks on a past session. The host app may open a modal or popup to display the session `{host}/session/{uuid}/{slug}`
|
|
126
|
+
|
|
127
|
+
- event `click:call`
|
|
128
|
+
- handler payload `{uuid: string, slug: string}`
|
|
129
|
+
|
|
130
|
+
Triggered when the user clicks on an ongoing call. The host app may open a modal or popup to join the call (`{host}/call/{slug}/{uuid}`)
|
|
131
|
+
|
|
132
|
+
- event `authentication:error`
|
|
133
|
+
- handler payload: `error: string`
|
|
134
|
+
|
|
135
|
+
Triggered when the user is not authenticated. The host app may display a login button in place of the iframe (`{host}/api/auth/login/{provider}`). Otherwise the iframe should display a "login" button with username / password method
|
|
136
|
+
|
|
137
|
+
- event `authentication:token`
|
|
138
|
+
- handler payload: `{token: string}`
|
|
139
|
+
|
|
140
|
+
Triggered when the user was successfully authenticated from an authenticate() call, or when the authentication token has changed within the app
|
|
141
|
+
|
|
142
|
+
**Example**
|
|
143
|
+
|
|
144
|
+
```jsx
|
|
145
|
+
viibe.on('authentication:error', (error) => {
|
|
146
|
+
console.log('authentication:error', error)
|
|
147
|
+
if (error === 'not authenticated') {
|
|
148
|
+
element.innerHTML = '';
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
viibe.on('click:session', (event) => {
|
|
153
|
+
console.log('click:session', event)
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
viibe.on('click:call', (event) => {
|
|
157
|
+
console.log('click:call', event)
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
viibe.on('resize', (event) => {
|
|
161
|
+
console.log('resize', event)
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Iframe API
|
|
166
|
+
|
|
167
|
+
### Global Query Parameters
|
|
168
|
+
|
|
169
|
+
*Dev note: to be handled globally, either in integration boot or in IntegrationLayout*
|
|
170
|
+
|
|
171
|
+
- `locale` **(string, optional)** : language to display the app into (e.g. `fr-FR`)
|
|
172
|
+
- `source` **(string, optional)** : identifier for the host application (e.g. `zendesk`, `mdynamics`, `salesforce`)
|
|
173
|
+
- `token` **(string, optional)** : a Viibe token to authenticate the user
|
|
174
|
+
|
|
175
|
+
**Legacy intercom parameters**
|
|
176
|
+
|
|
177
|
+
To be kept for backward compatibility, but should be isolated to the `postMessage` module
|
|
178
|
+
|
|
179
|
+
- `uuid` **(string)** : the UUID of the iframe, typically generated by viibe.js for interframe com
|
|
180
|
+
- `data` **(boolean)** : whether the host page will send initial postMessage data to the frame
|
|
181
|
+
- `resize` **(boolean)** : whether the frame should emit postMessages when its content changes size
|
|
182
|
+
|
|
183
|
+
### Call Parameters
|
|
184
|
+
|
|
185
|
+
*Dev note: to be handled at component level via a vue-router router*
|
|
186
|
+
|
|
187
|
+
The "call feature" is actually attached to a ticket, and allows to create, renew or schedule calls within this ticket
|
|
188
|
+
|
|
189
|
+
**Ticket identifier**
|
|
190
|
+
|
|
191
|
+
Tickets displayed in integration iframes are usually identified by the host id (or `thirdPartyId`). Either of the following params must be present.
|
|
192
|
+
|
|
193
|
+
- `thirdPartyId` **(string)** : an identifier of the host ticket to attach the Viibe ticket to (currently via `[ticket.integration.id](http://ticket.integration.id)`)
|
|
194
|
+
- `slug` **(string)** : a viibe ticket slug (currently not supported)
|
|
195
|
+
|
|
196
|
+
**Call modifiers**
|
|
197
|
+
|
|
198
|
+
The following parameters are passed from the host to the iframe to alter the current ticket
|
|
199
|
+
|
|
200
|
+
- `title` **(string, optional)** : the title of the ticket, saved to the viibe ticket every time a call is created or scheduled.
|
|
201
|
+
- `links` **(array, optional)** : a list of links to display in the "call" and "schedule" forms (i.e. to be added to the associated session on "call", "renew" or "schedule"). The array values are separated by comma (`,`) where each value has a `category` (e.g. `email`, `phone`, `username`, `expertise`, ...) and a `value` separated by a colon (example: `links=email:clement@viibe.co,username:charles,phone:+3361867999999`)
|
|
202
|
+
|
|
203
|
+
**UI options (call feature only)**
|
|
204
|
+
|
|
205
|
+
- `components` **(array, optional)**: a list of strings indicating the UI components to enable among `"call"`, `"schedule"` and `"session"` (default: all, example: `components=call,schedule,session`)
|
|
206
|
+
- `clipboard` **(boolean, optional)**: controls whether the "smart input" to add new links is visible or not
|
|
207
|
+
- `o2m` **(boolean, optional)** : controls whether the user can make one-to-many calls (i.e. can call expertises)
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{
|
|
1
|
+
(()=>{var t={579:t=>{function e(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}var n=function(){function t(e){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.host=e.host,this.origin=/^localhost/.test(this.host)?"http://".concat(this.host):"https://".concat(this.host),this.source=e.source,this.handlers={},window.addEventListener("message",this.onMessage.bind(this))}var n,o;return n=t,o=[{key:"createCallIframe",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!this.host)throw new Error("fillHost");if(!this.source)throw new Error("fillSource");var e=this.createCallUrl(t),n=document.createElement("iframe");return n.style="border: 0;width: calc(100% - 1rem);margin: 0.5rem;",n.src=e,n.allow="geolocation;autoplay;microphone;camera;midi;encrypted-media;clipboard-write;",n}},{key:"createCallUrl",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=t.clipboard,n=void 0!==e&&e,o=t.components,i=t.links,a=t.o2m,r=void 0!==a&&a,s=t.ticket,c=t.token,l=t.locale,u="".concat(this.origin,"/integration/features/call"),d=new URLSearchParams({resize:"true",source:this.source});if(null!=s&&s.title&&d.set("title",s.title),null!=s&&s.integration&&d.set("thirdPartyId",s.integration.id),i){var h=i instanceof Array?i.map((function(t){return"".concat(t.category,":").concat(t.value)})).join(","):String(i);d.set("links",h)}if(c&&d.set("token",c),l&&d.set("locale",l),o){var f=o instanceof Array?o.join(","):String(o);d.set("components",f)}return d.set("clipboard",Boolean(n)),d.set("o2m",Boolean(r)),u+"?".concat(d)}},{key:"isViibeEvent",value:function(t){return Boolean(t.isTrusted&&0===t.origin.indexOf(this.origin))}},{key:"dispatch",value:function(t,e){var n=this.handlers[t];return n?n.map((function(t){return t(e)})):null}},{key:"onMessage",value:function(t){return this.isViibeEvent(t)?t.data&&t.data.event&&t.data.payload?this.dispatch(t.data.event,t.data.payload):t.data&&t.data.token?this.dispatch("authentication:token",t.data.token):t.data&&t.data.error?this.dispatch("authentication:error",t.data.error):null:null}},{key:"on",value:function(t,e){(this.handlers[t]=this.handlers[t]||[]).push(e)}},{key:"authenticate",value:function(t){var e=this,n="".concat(this.origin,"/api/auth/login/").concat(t),o=window.screen.width/2-240,i=window.screen.height/2-300,a=["toolbar=no","location=no","directories=no","status=no","menubar=no","scrollbars=no","resizable=no","copyhistory=no","width=".concat(480),"height=".concat(600),"top=".concat(i),"left=".concat(o)];return new Promise((function(t,o){var i=window.open(n,"Authorization",a.join(", ")),r=setInterval((function(){i.closed&&(c(),s())}),100),s=function(){o("User closed window")},c=function(){window.removeEventListener("message",l),clearInterval(r)},l=function(n){if(!e.isViibeEvent(n))return null;n.data.token&&(c(),t(n.data.token)),n.data.error&&(c(),o(n.data.error))};window.addEventListener("message",l)}))}}],o&&e(n.prototype,o),t}();"undefined"!=typeof window&&(window.Viibe=n),t.exports=n}},e={},n=function n(o){var i=e[o];if(void 0!==i)return i.exports;var a=e[o]={exports:{}};return t[o](a,a.exports,n),a.exports}(579);module.exports=n})();
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viibe/impulse-integration-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "ViiBE integration in js native",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access":"public"
|
|
8
|
+
},
|
|
6
9
|
"scripts": {
|
|
7
10
|
"build": "webpack --mode production",
|
|
8
11
|
"prepublish": "npm run build",
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,9 @@ class Viibe {
|
|
|
4
4
|
this.host = options.host
|
|
5
5
|
this.origin = /^localhost/.test(this.host) ? `http://${this.host}` : `https://${this.host}`
|
|
6
6
|
this.source = options.source
|
|
7
|
+
this.handlers = {}
|
|
8
|
+
|
|
9
|
+
window.addEventListener('message', this.onMessage.bind(this))
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
createCallIframe(options = {}) {
|
|
@@ -21,45 +24,157 @@ class Viibe {
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
createCallUrl(options = {}) {
|
|
24
|
-
const {
|
|
27
|
+
const {
|
|
28
|
+
clipboard = false,
|
|
29
|
+
components,
|
|
30
|
+
links,
|
|
31
|
+
o2m = false,
|
|
32
|
+
ticket,
|
|
33
|
+
token,
|
|
34
|
+
locale
|
|
35
|
+
} = options
|
|
25
36
|
|
|
26
37
|
let src = `${this.origin}/integration/features/call`
|
|
27
38
|
|
|
28
|
-
const params =
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
const params = new URLSearchParams({
|
|
40
|
+
resize: 'true',
|
|
41
|
+
source: this.source,
|
|
42
|
+
});
|
|
31
43
|
|
|
32
|
-
if (ticket?.
|
|
44
|
+
if (ticket?.title) params.set('title', ticket.title)
|
|
45
|
+
if (ticket?.integration) params.set('thirdPartyId', ticket.integration.id)
|
|
33
46
|
|
|
34
47
|
if (links) {
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
let linksParams = (links instanceof Array)
|
|
49
|
+
? links.map(link => `${link.category}:${link.value}`).join(',')
|
|
50
|
+
: String(links)
|
|
51
|
+
|
|
52
|
+
params.set('links', linksParams)
|
|
37
53
|
}
|
|
38
54
|
|
|
39
|
-
if (token) params.
|
|
55
|
+
if (token) params.set('token', token)
|
|
56
|
+
|
|
57
|
+
if (locale) params.set('locale', locale)
|
|
58
|
+
|
|
59
|
+
if (components) {
|
|
60
|
+
let componentsParams = (components instanceof Array)
|
|
61
|
+
? components.join(',')
|
|
62
|
+
: String(components)
|
|
63
|
+
|
|
64
|
+
params.set('components', componentsParams)
|
|
65
|
+
}
|
|
40
66
|
|
|
41
|
-
|
|
67
|
+
params.set('clipboard', Boolean(clipboard))
|
|
68
|
+
params.set('o2m', Boolean(o2m))
|
|
42
69
|
|
|
43
|
-
src +=
|
|
70
|
+
src += `?${params}`;
|
|
44
71
|
|
|
45
72
|
return src
|
|
46
73
|
}
|
|
47
74
|
|
|
48
|
-
|
|
75
|
+
isViibeEvent(event) {
|
|
76
|
+
return Boolean(event.isTrusted && event.origin.indexOf(this.origin) === 0)
|
|
77
|
+
}
|
|
49
78
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
dispatch(eventName, payload) {
|
|
80
|
+
const eventHandlers = this.handlers[eventName];
|
|
81
|
+
if(!eventHandlers) {
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
return eventHandlers.map(handler => handler(payload))
|
|
85
|
+
}
|
|
53
86
|
|
|
54
|
-
|
|
87
|
+
onMessage(event) {
|
|
88
|
+
if(!this.isViibeEvent(event)) {
|
|
55
89
|
return null
|
|
56
90
|
}
|
|
57
91
|
|
|
58
|
-
|
|
92
|
+
if(event.data && event.data.event && event.data.payload) {
|
|
93
|
+
return this.dispatch(event.data.event, event.data.payload)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// FIXME: legacy authentication messages
|
|
97
|
+
if(event.data && event.data.token) {
|
|
98
|
+
return this.dispatch('authentication:token', event.data.token)
|
|
99
|
+
}
|
|
100
|
+
if(event.data && event.data.error) {
|
|
101
|
+
return this.dispatch('authentication:error', event.data.error)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null
|
|
59
105
|
}
|
|
60
106
|
|
|
61
|
-
|
|
107
|
+
on(eventName, handler) {
|
|
108
|
+
const eventHandlers = this.handlers[eventName] = this.handlers[eventName] || []
|
|
109
|
+
eventHandlers.push(handler);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
authenticate(provider) {
|
|
113
|
+
const src = `${this.origin}/api/auth/login/${provider}`
|
|
114
|
+
const width = 480
|
|
115
|
+
const height = 600
|
|
116
|
+
const TWO = 2
|
|
117
|
+
|
|
118
|
+
const left = (window.screen.width / TWO) - (width / TWO)
|
|
119
|
+
const top = (window.screen.height / TWO) - (height / TWO)
|
|
120
|
+
const windowParams = [
|
|
121
|
+
'toolbar=no',
|
|
122
|
+
'location=no',
|
|
123
|
+
'directories=no',
|
|
124
|
+
'status=no',
|
|
125
|
+
'menubar=no',
|
|
126
|
+
'scrollbars=no',
|
|
127
|
+
'resizable=no',
|
|
128
|
+
'copyhistory=no',
|
|
129
|
+
`width=${width}`,
|
|
130
|
+
`height=${height}`,
|
|
131
|
+
`top=${top}`,
|
|
132
|
+
`left=${left}`,
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
const win = window.open(src, 'Authorization', windowParams.join(', '))
|
|
137
|
+
|
|
138
|
+
const closeCheck = setInterval(() => {
|
|
139
|
+
if(win.closed) {
|
|
140
|
+
destroy();
|
|
141
|
+
onClose();
|
|
142
|
+
}
|
|
143
|
+
}, 100)
|
|
144
|
+
|
|
145
|
+
const onClose = () => {
|
|
146
|
+
reject('User closed window')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const destroy = () => {
|
|
150
|
+
window.removeEventListener('message', onMessage)
|
|
151
|
+
clearInterval(closeCheck)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const onMessage = event => {
|
|
155
|
+
if(!this.isViibeEvent(event)) {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
if(event.data.token) {
|
|
159
|
+
destroy();
|
|
160
|
+
resolve(event.data.token)
|
|
161
|
+
}
|
|
162
|
+
if(event.data.error) {
|
|
163
|
+
destroy();
|
|
164
|
+
reject(event.data.error)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
window.addEventListener('message', onMessage)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
}
|
|
62
172
|
|
|
63
|
-
|
|
173
|
+
}
|
|
64
174
|
|
|
65
|
-
|
|
175
|
+
if(typeof window !== 'undefined') {
|
|
176
|
+
window.Viibe = Viibe
|
|
177
|
+
}
|
|
178
|
+
if(typeof module !== 'undefined') {
|
|
179
|
+
module.exports = Viibe
|
|
180
|
+
}
|