@subiz/widget-v4 4.1.210 → 4.1.211
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 +21 -2
- package/assets/less/chatbox.less +9 -0
- package/assets/less/form.less +10 -0
- package/assets/less/message.less +114 -13
- package/assets/less/widget.less +21 -16
- package/compiled_src/call/css.js +1 -1
- package/compiled_src/chatbox/Buttons.js +7 -4
- package/compiled_src/chatbox/ChatButton.js +115 -29
- package/compiled_src/chatbox/Event.js +37 -5
- package/compiled_src/chatbox/Events.js +348 -129
- package/compiled_src/chatbox/FormAttachment.js +1 -1
- package/compiled_src/chatbox/Header.js +6 -2
- package/compiled_src/chatbox/InfoMessage.js +1 -1
- package/compiled_src/chatbox/MessageInput.js +24 -9
- package/compiled_src/chatbox/OrderAttachments.js +13 -13
- package/compiled_src/chatbox/QuoteMessage.js +3 -2
- package/compiled_src/chatbox/TextMessage.js +22 -18
- package/compiled_src/chatbox/Widget.js +200 -129
- package/compiled_src/chatbox/block.js +39 -29
- package/compiled_src/chatbox/conversation-history.js +44 -0
- package/compiled_src/chatbox/event-layout.js +59 -0
- package/compiled_src/chatbox/greeting.js +32 -0
- package/compiled_src/common.js +13 -9
- package/compiled_src/form/css.js +1 -1
- package/compiled_src/lib.js +22 -32
- package/compiled_src/notif/css.js +1 -1
- package/compiled_src/plugin.js +11 -18
- package/compiled_src/popup/css.js +1 -1
- package/compiled_src/popup/popup.js +1 -1
- package/compiled_src/store/api.js +0 -2
- package/compiled_src/store/api_fake.js +1 -1
- package/compiled_src/store/attachments.js +28 -0
- package/compiled_src/store/chain.js +24 -10
- package/compiled_src/store/kv.js +9 -13
- package/compiled_src/store/lsdb.js +1 -1
- package/compiled_src/store/store.js +356 -117
- package/compiled_src/util/common.js +48 -41
- package/compiled_src/util/event.js +15 -0
- package/compiled_src/util/pubsub.js +28 -13
- package/compiled_src/util/url.js +18 -0
- package/dist/.HASH +1 -0
- package/dist/127.js +13 -13
- package/dist/132.js +1 -1
- package/dist/1a0ddc53cbb.127.js +1 -0
- package/dist/{1a0d80f6e72.132.js → 1a0ddc53cbb.132.js} +1 -1
- package/dist/1a0ddc53cbb.278.js +1 -0
- package/dist/{1a0d80f6e72.339.js → 1a0ddc53cbb.339.js} +1 -1
- package/dist/1a0ddc53cbb.app.js +1 -0
- package/dist/{1a0d80f6e72.chat.html → 1a0ddc53cbb.chat.html} +1 -1
- package/dist/1a0ddc53cbb.chat.js +1 -0
- package/dist/{1a0d80f6e72.form.html → 1a0ddc53cbb.form.html} +1 -1
- package/dist/1a0ddc53cbb.form.js +1 -0
- package/dist/{1a0d80f6e72.index.html → 1a0ddc53cbb.index.html} +1 -1
- package/dist/{1a0d80f6e72.library.html → 1a0ddc53cbb.library.html} +1 -1
- package/dist/1a0ddc53cbb.library.js +1 -0
- package/dist/{1a0d80f6e72.preview.html → 1a0ddc53cbb.preview.html} +1 -1
- package/dist/1a0ddc53cbb.preview.js +1 -0
- package/dist/1a0ddc53cbb.webview.js +1 -0
- package/dist/screenshoot.js +1492 -616
- package/package.json +10 -3
- package/src/chatbox/Buttons.js +5 -4
- package/src/chatbox/ChatButton.js +120 -39
- package/src/chatbox/Event.js +38 -5
- package/src/chatbox/Events.js +306 -134
- package/src/chatbox/FormAttachment.js +1 -1
- package/src/chatbox/Header.js +4 -4
- package/src/chatbox/InfoMessage.js +1 -1
- package/src/chatbox/MessageInput.js +15 -10
- package/src/chatbox/OrderAttachments.js +13 -13
- package/src/chatbox/QuoteMessage.js +3 -2
- package/src/chatbox/TextMessage.js +21 -20
- package/src/chatbox/Widget.js +150 -150
- package/src/chatbox/block.js +29 -27
- package/src/chatbox/conversation-history.js +37 -0
- package/src/chatbox/event-layout.js +72 -0
- package/src/chatbox/greeting.js +13 -0
- package/src/common.js +15 -9
- package/src/lib.js +22 -35
- package/src/plugin.js +11 -13
- package/src/popup/popup.js +1 -1
- package/src/store/api.js +0 -2
- package/src/store/api_fake.js +1 -1
- package/src/store/attachments.js +21 -0
- package/src/store/chain.js +25 -12
- package/src/store/kv.js +10 -19
- package/src/store/lsdb.js +1 -1
- package/src/store/store.js +313 -130
- package/src/util/common.js +48 -43
- package/src/util/event.js +7 -0
- package/src/util/pubsub.js +27 -8
- package/src/util/url.js +14 -0
- package/test.cjs +59 -0
- package/assets/less/contact.less +0 -126
- package/compiled_src/util/common.test.js +0 -414
- package/compiled_src/util/event.test.js +0 -38
- package/compiled_src/util/extractor.test.js +0 -299
- package/compiled_src/util/extractor.test2.js +0 -61
- package/compiled_src/util/gate.test.js +0 -18
- package/compiled_src/util/pubsub.test.js +0 -35
- package/dist/1a0d80f6e72.127.js +0 -1
- package/dist/1a0d80f6e72.278.js +0 -1
- package/dist/1a0d80f6e72.74.js +0 -1
- package/dist/1a0d80f6e72.997.js +0 -1
- package/dist/1a0d80f6e72.app.js +0 -1
- package/dist/1a0d80f6e72.chat.js +0 -1
- package/dist/1a0d80f6e72.form.js +0 -1
- package/dist/1a0d80f6e72.library.js +0 -1
- package/dist/1a0d80f6e72.preview.js +0 -1
- package/dist/1a0d80f6e72.webview.js +0 -1
- package/dist/74.js +0 -376
- package/dist/997.js +0 -37
- package/dist/HASH +0 -1
- package/src/contact/contact.js +0 -400
- package/src/contact/css.js +0 -1
- package/src/util/common.test.js +0 -301
- package/src/util/event.test.js +0 -31
- package/src/util/extractor.test.js +0 -240
- package/src/util/extractor.test2.js +0 -56
- package/src/util/gate.test.js +0 -20
- package/src/util/pubsub.test.js +0 -37
- /package/dist/{1a0d80f6e72.185.js → 1a0ddc53cbb.185.js} +0 -0
- /package/dist/{1a0d80f6e72.27.js → 1a0ddc53cbb.27.js} +0 -0
- /package/dist/{1a0d80f6e72.459.js → 1a0ddc53cbb.459.js} +0 -0
- /package/dist/{1a0d80f6e72.490.js → 1a0ddc53cbb.490.js} +0 -0
package/src/chatbox/block.js
CHANGED
|
@@ -2,6 +2,7 @@ import sb from '../util/index.js'
|
|
|
2
2
|
import Emoji from './Emoji.js'
|
|
3
3
|
import {Com} from '../common.js'
|
|
4
4
|
import {h} from 'preact'
|
|
5
|
+
import {safeUrl} from '../util/url.js'
|
|
5
6
|
|
|
6
7
|
export default class Block extends Com {
|
|
7
8
|
constructor(props) {
|
|
@@ -43,9 +44,8 @@ export default class Block extends Com {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
if (block.type == 'heading') {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
ele.tag = 'h' + block.level
|
|
47
|
+
let level = Math.max(1, Math.min(6, Math.floor(Number(block.level) || 1)))
|
|
48
|
+
ele.tag = 'h' + level
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (block.type == 'paragraph' || block.type == 'section' || block.type == 'div') ele.tag = 'div'
|
|
@@ -61,8 +61,10 @@ export default class Block extends Com {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
if (block.
|
|
64
|
+
let decorations = []
|
|
65
|
+
if (block.underline) decorations.push('underline')
|
|
66
|
+
if (block.strike_through) decorations.push('line-through')
|
|
67
|
+
if (decorations.length) ele.style += 'text-decoration: ' + decorations.join(' ') + ' !important;'
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
if (block.type == 'dynamic-field') {
|
|
@@ -72,7 +74,7 @@ export default class Block extends Com {
|
|
|
72
74
|
|
|
73
75
|
if (block.type == 'link') {
|
|
74
76
|
ele.tag = 'a'
|
|
75
|
-
ele.attrs['href'] = block.href
|
|
77
|
+
ele.attrs['href'] = safeUrl(block.href)
|
|
76
78
|
ele.attrs['target'] = block.target
|
|
77
79
|
ele.attrs['title'] = block.title
|
|
78
80
|
}
|
|
@@ -80,15 +82,15 @@ export default class Block extends Com {
|
|
|
80
82
|
ele.tag = 'span'
|
|
81
83
|
ele.class = 'mention'
|
|
82
84
|
|
|
83
|
-
let id = (block
|
|
85
|
+
let id = sb.get(block, 'attrs.id', '')
|
|
84
86
|
|
|
85
|
-
if (
|
|
86
|
-
ele.text = '@' + this
|
|
87
|
+
if (id == '*' || id == 'all') {
|
|
88
|
+
ele.text = '@' + (this.props.t ? this.props.t('all') : 'all')
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
if (id
|
|
90
|
-
let ag = store.matchAgent(id)
|
|
91
|
-
let name = sb.getAgentDisplayName(
|
|
91
|
+
if (typeof id === 'string' && /^(ag|at)/.test(id) && this.props.store) {
|
|
92
|
+
let ag = this.props.store.matchAgent(id)
|
|
93
|
+
let name = sb.getAgentDisplayName(this.props.store.matchAccount(), ag)
|
|
92
94
|
ele.text = '@' + name
|
|
93
95
|
}
|
|
94
96
|
}
|
|
@@ -96,19 +98,16 @@ export default class Block extends Com {
|
|
|
96
98
|
if (block.type == 'emoji') {
|
|
97
99
|
let code = ''
|
|
98
100
|
if (sb.size(block.attrs) > 0) code = block.attrs['code']
|
|
99
|
-
if (code
|
|
101
|
+
if (!code) code = block.text || block.Text
|
|
100
102
|
if (code != '') {
|
|
101
103
|
ele.tag = 'Emoji' //
|
|
102
104
|
ele.attrs['emojiCode'] = code
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
if (block.type == 'table') {
|
|
107
|
-
return null
|
|
108
|
-
}
|
|
109
108
|
if (block.type === 'image') {
|
|
110
109
|
ele.tag = 'img'
|
|
111
|
-
ele.attrs['src'] = sb.get(block, 'image.url')
|
|
110
|
+
ele.attrs['src'] = safeUrl(sb.get(block, 'image.url'), true)
|
|
112
111
|
ele.attrs['alt'] = block.alt_text
|
|
113
112
|
}
|
|
114
113
|
if (block.type == 'hard_break') {
|
|
@@ -131,9 +130,11 @@ export default class Block extends Com {
|
|
|
131
130
|
) {
|
|
132
131
|
url = '//' + url
|
|
133
132
|
}
|
|
133
|
+
url = safeUrl(url)
|
|
134
|
+
if (!url) return tag.text
|
|
134
135
|
|
|
135
136
|
return (
|
|
136
|
-
<a href={url} target='_blank' onClick={(e) => this.props.onLink(e, url)}>
|
|
137
|
+
<a href={url} target='_blank' rel='noopener noreferrer' onClick={(e) => this.props.onLink && this.props.onLink(e, url)}>
|
|
137
138
|
{tag.text}
|
|
138
139
|
</a>
|
|
139
140
|
)
|
|
@@ -148,11 +149,13 @@ export default class Block extends Com {
|
|
|
148
149
|
}
|
|
149
150
|
switch (ele.tag) {
|
|
150
151
|
case 'img':
|
|
152
|
+
if (!ele.attrs.src) return block.alt_text || null
|
|
151
153
|
return (
|
|
152
154
|
<a
|
|
153
155
|
class={this.props.textMessage ? 'message-text-inline-image-wrapper' : ''}
|
|
154
156
|
href={sb.get(ele, 'attrs.src')}
|
|
155
157
|
target='_blank'
|
|
158
|
+
rel='noopener noreferrer'
|
|
156
159
|
>
|
|
157
160
|
<img
|
|
158
161
|
class={this.props.textMessage ? 'message-text-inline-image' : ''}
|
|
@@ -186,22 +189,25 @@ export default class Block extends Com {
|
|
|
186
189
|
case 'li':
|
|
187
190
|
return <li class={this.props.textMessage ? 'text-message-li' : ''}>{$body}</li>
|
|
188
191
|
case 'span':
|
|
189
|
-
return <span>{$body}</span>
|
|
192
|
+
return <span class={ele.class} style={ele.style}>{$body}</span>
|
|
190
193
|
case 'em':
|
|
191
|
-
return <em>{$body}</em>
|
|
194
|
+
return <em style={ele.style}>{$body}</em>
|
|
192
195
|
case 'b':
|
|
193
|
-
return <b>{$body}</b>
|
|
196
|
+
return <b style={ele.style}>{$body}</b>
|
|
194
197
|
case 'hr':
|
|
195
198
|
return <hr />
|
|
196
199
|
case 'br':
|
|
197
200
|
return <br />
|
|
198
201
|
case 'a':
|
|
202
|
+
if (!ele.attrs.href) return <span>{$body}</span>
|
|
199
203
|
return (
|
|
200
204
|
<a
|
|
201
205
|
class={this.props.textMessage ? 'message-text-link' : ''}
|
|
202
206
|
href={ele.attrs['href']}
|
|
203
207
|
target={this.props.textMessage ? '_blank' : ele.attrs['target']}
|
|
204
208
|
title={ele.attrs['title']}
|
|
209
|
+
rel='noopener noreferrer'
|
|
210
|
+
onClick={(e) => this.props.onLink && this.props.onLink(e, ele.attrs.href)}
|
|
205
211
|
>
|
|
206
212
|
{$body}
|
|
207
213
|
</a>
|
|
@@ -210,15 +216,11 @@ export default class Block extends Com {
|
|
|
210
216
|
return <Emoji emojiCode={ele.attrs['emojiCode']} />
|
|
211
217
|
}
|
|
212
218
|
|
|
213
|
-
|
|
219
|
+
// Unknown containers should retain their readable content.
|
|
220
|
+
return $body
|
|
214
221
|
}
|
|
215
222
|
|
|
216
223
|
render() {
|
|
217
224
|
return this.renderBlock(this.props.block)
|
|
218
225
|
}
|
|
219
226
|
}
|
|
220
|
-
|
|
221
|
-
function absLink(url) {
|
|
222
|
-
if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('//')) return 'http://' + url
|
|
223
|
-
return url
|
|
224
|
-
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import sb from '../util/index.js'
|
|
2
|
+
|
|
3
|
+
// Creation order is stable even when an older conversation receives a new reply.
|
|
4
|
+
export function conversationHistory(store, cid) {
|
|
5
|
+
if (!cid) return []
|
|
6
|
+
const current = store.matchConvo(cid)
|
|
7
|
+
if (!current || !current.created) return [current || {id: cid}]
|
|
8
|
+
const conversations = sb.map(store.matchConvo('*'))
|
|
9
|
+
const unique = new Map()
|
|
10
|
+
for (const convo of conversations) {
|
|
11
|
+
if (!convo || !convo.id || convo.id === cid || convo.id.startsWith('cslight')) continue
|
|
12
|
+
if (current.account_id && convo.account_id !== current.account_id) continue
|
|
13
|
+
if (convo.created && sb.getMs(convo.created) < sb.getMs(current.created)) unique.set(convo.id, convo)
|
|
14
|
+
}
|
|
15
|
+
return [...unique.values()].sort((a, b) => sb.getMs(a.created) - sb.getMs(b.created) || a.id.localeCompare(b.id)).concat(current)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function conversationEvents(store, convo) {
|
|
19
|
+
const events = sb.orderBy(sb.map(store.listEvents(convo.id)), 'created', 'asc')
|
|
20
|
+
const unique = new Map()
|
|
21
|
+
for (const ev of events) {
|
|
22
|
+
const key = conversationEventKey(ev)
|
|
23
|
+
const previous = unique.get(key)
|
|
24
|
+
if (!previous) unique.set(key, ev)
|
|
25
|
+
// History can arrive before the optimistic event is reconciled by the socket.
|
|
26
|
+
// Prefer the confirmed payload while keeping the original message position.
|
|
27
|
+
else if (previous.id === previous.source_event_id && ev.id !== ev.source_event_id) unique.set(key, {...ev, created: previous.created})
|
|
28
|
+
}
|
|
29
|
+
return [...unique.values()].map(ev => ({ev}))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function conversationEventKey(ev) {
|
|
33
|
+
// A send can also produce conversation_joined with the same source_event_id.
|
|
34
|
+
// Only message copies use that ID for optimistic acknowledgement reconciliation.
|
|
35
|
+
const id = ev.type === 'message_sent' ? ev.source_event_id || ev.id : ev.id
|
|
36
|
+
return ev.type + ':' + id
|
|
37
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import sb from '../util/index.js'
|
|
2
|
+
|
|
3
|
+
//_mode = lead
|
|
4
|
+
export function processEvents(convo, events) {
|
|
5
|
+
events = events.filter((event) => visibleEvent(event.ev))
|
|
6
|
+
if (events.length < 1) return
|
|
7
|
+
|
|
8
|
+
// show time for the first msg sent
|
|
9
|
+
// insert time
|
|
10
|
+
var lastAgentEvent
|
|
11
|
+
for (var i = 1; i < events.length; i++) {
|
|
12
|
+
let curEvent = events[i]
|
|
13
|
+
let lastEvent = events[i - 1]
|
|
14
|
+
if (sb.get(curEvent, 'ev.by.type') != 'user' && !curEvent.is_typing) {
|
|
15
|
+
lastAgentEvent = curEvent
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (visibleEvent(curEvent.ev)) {
|
|
19
|
+
let elapsedSec = sb.unixSec(curEvent.ev.created) - sb.unixSec(lastEvent.ev.created)
|
|
20
|
+
if (elapsedSec > 300) curEvent.show_time = true // 5 mins
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (lastAgentEvent) lastAgentEvent.show_time = true
|
|
25
|
+
|
|
26
|
+
for (var i = 0; i < events.length; i++) {
|
|
27
|
+
let curEvent = events[i]
|
|
28
|
+
if (sb.get(curEvent, 'ev.type') === 'message_sent') {
|
|
29
|
+
curEvent.show_time = true
|
|
30
|
+
break
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let lastMsgSentIdx = -1
|
|
35
|
+
// insert lead
|
|
36
|
+
for (var i = 0; i < events.length; i++) {
|
|
37
|
+
let curEvent = events[i]
|
|
38
|
+
let prevEvent = events[i - 1]
|
|
39
|
+
let nextEvent = events[i + 1]
|
|
40
|
+
|
|
41
|
+
if (curEvent.ev.type === 'message_sent') {
|
|
42
|
+
// ignore fake typing message_sent event at last
|
|
43
|
+
lastMsgSentIdx = i
|
|
44
|
+
curEvent.mode = 'lead'
|
|
45
|
+
if (
|
|
46
|
+
sb.get(prevEvent, 'ev.type') === 'message_sent' &&
|
|
47
|
+
sb.get(prevEvent, 'ev.by.id') === sb.get(curEvent, 'ev.by.id')
|
|
48
|
+
) {
|
|
49
|
+
curEvent.mode = 'follow'
|
|
50
|
+
}
|
|
51
|
+
if (
|
|
52
|
+
sb.get(nextEvent, 'ev.type') !== 'message_sent' ||
|
|
53
|
+
sb.get(nextEvent, 'ev.by.id') !== sb.get(curEvent, 'ev.by.id')
|
|
54
|
+
) {
|
|
55
|
+
curEvent.is_end = true
|
|
56
|
+
}
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (lastMsgSentIdx > -1) {
|
|
61
|
+
events[lastMsgSentIdx].is_last_message_send = true
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function visibleEvent(ev) {
|
|
66
|
+
let type = sb.get(ev, 'type')
|
|
67
|
+
if (type === 'message_sent') return true
|
|
68
|
+
if (type === 'conversation_state_updated') {
|
|
69
|
+
return sb.get(ev, 'data.conversation.state') == 'ended'
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import sb from '../util/index.js'
|
|
2
|
+
|
|
3
|
+
export function widgetGreeting(chatbox, t, created = sb.Date.now()) {
|
|
4
|
+
const greeting = sb.get(chatbox, 'greeting', {})
|
|
5
|
+
const localized = t.i18n ? t.i18n(greeting.greeting || {}) : ''
|
|
6
|
+
const text = sb.trim(localized) || sb.trim(greeting.message) || sb.trim(t('team_intro')) || t('empty_convo_text')
|
|
7
|
+
return {
|
|
8
|
+
id: 'widget-greeting-' + sb.randomString(24),
|
|
9
|
+
type: 'message_sent', created,
|
|
10
|
+
by: {type: 'system', id: 'system', ...(greeting.agent_id ? {display_type: 'agent', display_id: greeting.agent_id} : {})},
|
|
11
|
+
data: {message: {text, format: 'plaintext'}},
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/common.js
CHANGED
|
@@ -5,15 +5,21 @@ export class Com extends Component {
|
|
|
5
5
|
constructor(props) {
|
|
6
6
|
super(props)
|
|
7
7
|
this._isMounted = true
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
8
|
+
this._subscriptions = new Set()
|
|
9
|
+
const onUnmount = this.componentWillUnmount
|
|
10
|
+
this.componentWillUnmount = () => {
|
|
11
|
+
if (!this._isMounted) return
|
|
12
|
+
this._isMounted = false
|
|
13
|
+
for (const unsubscribe of this._subscriptions) unsubscribe()
|
|
14
|
+
this._subscriptions.clear()
|
|
15
|
+
if (onUnmount) onUnmount.call(this)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
onDispose(cleanup) {
|
|
20
|
+
if (this._isMounted) this._subscriptions.add(cleanup)
|
|
21
|
+
else cleanup()
|
|
22
|
+
return () => this._subscriptions.delete(cleanup)
|
|
17
23
|
}
|
|
18
24
|
}
|
|
19
25
|
|
package/src/lib.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Store from './store/store.js'
|
|
2
2
|
import sb from './util/index.js'
|
|
3
3
|
import PluginMgr from './plugin.js'
|
|
4
|
+
import {normalizeCdpEvent} from './util/event.js'
|
|
4
5
|
|
|
5
6
|
/*
|
|
6
7
|
var $link = document.createElement('link')
|
|
@@ -14,6 +15,7 @@ sb.preloadWebfonts(['Inter'], [400, 500])
|
|
|
14
15
|
class Widget {
|
|
15
16
|
constructor(ApiURL, FileURL, RealtimeURL, inmem, fake) {
|
|
16
17
|
this.impressionTokens = {}
|
|
18
|
+
this._containerTimers = new Set()
|
|
17
19
|
this.domGate = new sb.Gate()
|
|
18
20
|
this.store = new Store(ApiURL, FileURL, RealtimeURL, inmem, fake)
|
|
19
21
|
|
|
@@ -37,6 +39,17 @@ class Widget {
|
|
|
37
39
|
cb()
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
watchContainer(container) {
|
|
43
|
+
const manager = this.pluginMgr
|
|
44
|
+
const timer = sb.setInterval(() => {
|
|
45
|
+
if (!this.store.isDead() && container && container.isConnected) return
|
|
46
|
+
manager.destroy()
|
|
47
|
+
sb.clearInterval(timer)
|
|
48
|
+
this._containerTimers.delete(timer)
|
|
49
|
+
}, 1000)
|
|
50
|
+
this._containerTimers.add(timer)
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
setAIDevMode(mode) {
|
|
41
54
|
this.store.setAIDevMode(mode)
|
|
42
55
|
}
|
|
@@ -60,12 +73,7 @@ class Widget {
|
|
|
60
73
|
if (!$container) $container = document.body
|
|
61
74
|
this.pluginMgr = new PluginMgr(this, $container)
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
// check if the dom is deleted
|
|
65
|
-
if ($container && $container.isConnected) return
|
|
66
|
-
this.pluginMgr.destroy()
|
|
67
|
-
sb.clearInterval(clean)
|
|
68
|
-
}, 1000)
|
|
76
|
+
this.watchContainer($container)
|
|
69
77
|
this.domGate.open()
|
|
70
78
|
return user
|
|
71
79
|
}
|
|
@@ -92,12 +100,7 @@ class Widget {
|
|
|
92
100
|
if (!$container) $container = document.body || document
|
|
93
101
|
this.pluginMgr = new PluginMgr(this, $container)
|
|
94
102
|
|
|
95
|
-
|
|
96
|
-
// check if the dom is deleted
|
|
97
|
-
if ($container && $container.isConnected) return
|
|
98
|
-
this.pluginMgr.destroy()
|
|
99
|
-
sb.clearInterval(clean)
|
|
100
|
-
}, 1000)
|
|
103
|
+
this.watchContainer($container)
|
|
101
104
|
this.domGate.open()
|
|
102
105
|
return user
|
|
103
106
|
}
|
|
@@ -111,12 +114,7 @@ class Widget {
|
|
|
111
114
|
if (!$container) $container = document.body || document
|
|
112
115
|
this.pluginMgr = new PluginMgr(this, $container)
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
// check if the dom is deleted
|
|
116
|
-
if ($container && $container.isConnected) return
|
|
117
|
-
this.pluginMgr.destroy()
|
|
118
|
-
sb.clearInterval(clean)
|
|
119
|
-
}, 1000)
|
|
117
|
+
this.watchContainer($container)
|
|
120
118
|
this.domGate.open()
|
|
121
119
|
cb && cb(outmask)
|
|
122
120
|
return outmask
|
|
@@ -144,12 +142,7 @@ class Widget {
|
|
|
144
142
|
if (!$container) $container = document.body || document
|
|
145
143
|
this.pluginMgr = new PluginMgr(this, $container)
|
|
146
144
|
|
|
147
|
-
|
|
148
|
-
// check if the dom is deleted
|
|
149
|
-
if ($container && $container.isConnected) return
|
|
150
|
-
this.pluginMgr.destroy()
|
|
151
|
-
sb.clearInterval(clean)
|
|
152
|
-
}, 1000)
|
|
145
|
+
this.watchContainer($container)
|
|
153
146
|
this.domGate.open()
|
|
154
147
|
if (typeof sbz_info != 'undefined') sbz_info.set_account_done = sb.Date.now()
|
|
155
148
|
|
|
@@ -181,7 +174,7 @@ class Widget {
|
|
|
181
174
|
|
|
182
175
|
async createTicket(ticket) {
|
|
183
176
|
await this.domGate.entry()
|
|
184
|
-
return this.createTicket(ticket)
|
|
177
|
+
return this.store.createTicket(ticket)
|
|
185
178
|
}
|
|
186
179
|
|
|
187
180
|
expandWidget(view, convo) {
|
|
@@ -274,7 +267,7 @@ class Widget {
|
|
|
274
267
|
if (req.bot_id) {
|
|
275
268
|
let botid = req.bot_id
|
|
276
269
|
delete req.bot_id
|
|
277
|
-
if (!sb.find(members, (m) =>
|
|
270
|
+
if (!sb.find(members, (m) => m.id == botid)) {
|
|
278
271
|
members.push({type: 'bot', id: botid, bot_version: req.bot_version})
|
|
279
272
|
}
|
|
280
273
|
}
|
|
@@ -343,6 +336,9 @@ class Widget {
|
|
|
343
336
|
}
|
|
344
337
|
|
|
345
338
|
unload() {
|
|
339
|
+
for (const timer of this._containerTimers) sb.clearInterval(timer)
|
|
340
|
+
this._containerTimers.clear()
|
|
341
|
+
this.subs = []
|
|
346
342
|
// this.store.overridePlugin([])
|
|
347
343
|
if (this.pluginMgr) this.pluginMgr.destroy()
|
|
348
344
|
this.store.destroy()
|
|
@@ -403,15 +399,6 @@ class Widget {
|
|
|
403
399
|
}
|
|
404
400
|
}
|
|
405
401
|
|
|
406
|
-
function normalizeCdpEvent(type, payload, options) {
|
|
407
|
-
if (type && typeof type === 'object') return type
|
|
408
|
-
|
|
409
|
-
let data = {type: type}
|
|
410
|
-
if (payload !== undefined) data.payload = payload
|
|
411
|
-
if (options && typeof options === 'object') data = Object.assign({}, options, data)
|
|
412
|
-
return data
|
|
413
|
-
}
|
|
414
|
-
|
|
415
402
|
// for debugging purpose
|
|
416
403
|
window.__sbz_WIDGET = Widget
|
|
417
404
|
export default Widget
|
package/src/plugin.js
CHANGED
|
@@ -4,7 +4,6 @@ import {h, render, createRef} from 'preact'
|
|
|
4
4
|
|
|
5
5
|
// const LoadChatbox = () => import(/*webpackChunkName: "chatbox" */ './chatbox/main.js')
|
|
6
6
|
import Chatbox from './chatbox/main.js'
|
|
7
|
-
const LoadContact = () => import(/*webpackChunkName: "contact" */ './contact/contact.js')
|
|
8
7
|
const LoadPopup = () => import(/*webpackChunkName: "popup" */ './popup/popup.js')
|
|
9
8
|
const LoadNotif = () => import(/*webpackChunkName: "notif" */ './notif/notif.js')
|
|
10
9
|
|
|
@@ -59,7 +58,6 @@ function Plugin(plugin, store, $container, lib) {
|
|
|
59
58
|
|
|
60
59
|
// let Chatbox = null
|
|
61
60
|
let Popup = null
|
|
62
|
-
let Contact = null
|
|
63
61
|
let Notif = null
|
|
64
62
|
|
|
65
63
|
var showCurrentChatbox = (p) => lib.showWidget(p)
|
|
@@ -107,11 +105,6 @@ function Plugin(plugin, store, $container, lib) {
|
|
|
107
105
|
)
|
|
108
106
|
}
|
|
109
107
|
|
|
110
|
-
if (plugin.type === 'contact') {
|
|
111
|
-
if (!Contact) Contact = (await LoadContact()).default
|
|
112
|
-
render(<Contact destroy={_destroy} t={this.t} plugin={plugin} store={store} ref={_ref} />, $div)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
108
|
domGate.open()
|
|
116
109
|
}
|
|
117
110
|
|
|
@@ -123,10 +116,11 @@ function Plugin(plugin, store, $container, lib) {
|
|
|
123
116
|
|
|
124
117
|
var _destroy = false
|
|
125
118
|
this.destroy = () => {
|
|
119
|
+
if (_destroy) return
|
|
126
120
|
_destroy = true
|
|
127
121
|
render(null, $div) // trigger unmount
|
|
128
122
|
|
|
129
|
-
$div.parentNode.removeChild($div)
|
|
123
|
+
if ($div.parentNode) $div.parentNode.removeChild($div)
|
|
130
124
|
// remove div
|
|
131
125
|
}
|
|
132
126
|
|
|
@@ -229,14 +223,15 @@ export default class PluginMgr {
|
|
|
229
223
|
this.pluginM = {}
|
|
230
224
|
|
|
231
225
|
this._destroy = false
|
|
226
|
+
this._subscriptions = []
|
|
232
227
|
|
|
233
|
-
lib.store.onHighlight({_isMounted: true}, (id, comref) => {
|
|
228
|
+
this._subscriptions.push(lib.store.onHighlight({_isMounted: true}, (id, comref) => {
|
|
234
229
|
let plugin = this.pluginM[id]
|
|
235
230
|
if (!plugin) return
|
|
236
231
|
plugin.highlight(comref)
|
|
237
|
-
})
|
|
232
|
+
}))
|
|
238
233
|
|
|
239
|
-
lib.store.onUpdate({_isMounted: true}, () => {
|
|
234
|
+
this._subscriptions.push(lib.store.onUpdate({_isMounted: true}, () => {
|
|
240
235
|
if (this._destroy) return
|
|
241
236
|
let pluginM = this.lib.store.matchPlugin()
|
|
242
237
|
let foundchatbox = false
|
|
@@ -259,7 +254,7 @@ export default class PluginMgr {
|
|
|
259
254
|
this.pluginM[id].destroy()
|
|
260
255
|
delete this.pluginM[id]
|
|
261
256
|
})
|
|
262
|
-
})
|
|
257
|
+
}))
|
|
263
258
|
|
|
264
259
|
let pluginM = this.lib.store.matchPlugin()
|
|
265
260
|
if (typeof sbz_info != 'undefined') sbz_info['plugins'] = pluginM
|
|
@@ -351,11 +346,14 @@ export default class PluginMgr {
|
|
|
351
346
|
}
|
|
352
347
|
|
|
353
348
|
destroy() {
|
|
349
|
+
if (this._destroy) return
|
|
350
|
+
this._destroy = true
|
|
351
|
+
for (const unsubscribe of this._subscriptions) unsubscribe()
|
|
352
|
+
this._subscriptions = []
|
|
354
353
|
sb.map(this.pluginM, (conf, id) => {
|
|
355
354
|
conf.destroy()
|
|
356
355
|
delete this.pluginM[id]
|
|
357
356
|
})
|
|
358
357
|
|
|
359
|
-
this._destroy = true
|
|
360
358
|
}
|
|
361
359
|
}
|
package/src/popup/popup.js
CHANGED
|
@@ -63,7 +63,7 @@ export default class Popup extends Com {
|
|
|
63
63
|
for (var i = 0; i < fonts.length; i++) {
|
|
64
64
|
try {
|
|
65
65
|
if (document.fonts.check('normal 16px ' + fonts[i])) ready[fonts[i]] = true
|
|
66
|
-
if (Object.keys(
|
|
66
|
+
if (Object.keys(ready).length == fonts.length) return
|
|
67
67
|
} catch (e) {}
|
|
68
68
|
}
|
|
69
69
|
await sb.sleep(200)
|
package/src/store/api.js
CHANGED
|
@@ -39,7 +39,6 @@ export default class {
|
|
|
39
39
|
|
|
40
40
|
this.FileURL = FileURL
|
|
41
41
|
this.ApiURL = ApiURL
|
|
42
|
-
this.bugReq = ajax.setBaseUrl('https://api5.subiz.com.vn/4.1').setContentType('text/plain')
|
|
43
42
|
this.api41 = ajax
|
|
44
43
|
.setBaseUrl('https://api.sbz.vn/4.1')
|
|
45
44
|
.withCredentials(true)
|
|
@@ -230,7 +229,6 @@ export default class {
|
|
|
230
229
|
return {error: 'invalid_auth'}
|
|
231
230
|
}
|
|
232
231
|
|
|
233
|
-
sendBugReport = (str) => this.bugReq.post('/bugs', str)
|
|
234
232
|
|
|
235
233
|
listConversations = (_) =>
|
|
236
234
|
this.apireq
|
package/src/store/api_fake.js
CHANGED
|
@@ -208,6 +208,7 @@ function makeFakeMessages(accid, convoid, userid, agentid) {
|
|
|
208
208
|
},
|
|
209
209
|
{
|
|
210
210
|
id: 'evqyiyjmzatvtgpdmmpzwnwgn',
|
|
211
|
+
source_event_id: 'wrueysyjbhqrkiwhsejpqrwqrdkkyaibajdw',
|
|
211
212
|
account_id: accid,
|
|
212
213
|
created: 1616213202685,
|
|
213
214
|
type: 'message_sent',
|
|
@@ -217,7 +218,6 @@ function makeFakeMessages(accid, convoid, userid, agentid) {
|
|
|
217
218
|
conversation_id: convoid,
|
|
218
219
|
text: 'Xin chào',
|
|
219
220
|
format: 'plaintext',
|
|
220
|
-
idempotency_key: 'wrueysyjbhqrkiwhsejpqrwqrdkkyaibajdw',
|
|
221
221
|
integration_id: inteid,
|
|
222
222
|
pongs: [{member_id: userid, type: 'seen', created: 1616213203068}],
|
|
223
223
|
},
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Keep upload errors across the entire batch. A later successful upload must
|
|
2
|
+
// not turn an incomplete message into a successful one.
|
|
3
|
+
export async function uploadAttachments(attachments, upload) {
|
|
4
|
+
let error, errorMessage
|
|
5
|
+
for (const attachment of attachments) {
|
|
6
|
+
try {
|
|
7
|
+
const result = await upload(attachment._file)
|
|
8
|
+
if (result.body && result.body.code === 'invalid_field_size') {
|
|
9
|
+
errorMessage = 'cannot_send_too_big_file'
|
|
10
|
+
}
|
|
11
|
+
if (result.error || !result.url) {
|
|
12
|
+
error = error || result.error || 'upload_failed'
|
|
13
|
+
continue
|
|
14
|
+
}
|
|
15
|
+
attachment.url = result.url
|
|
16
|
+
} catch (err) {
|
|
17
|
+
error = error || err
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return {error, errorMessage}
|
|
21
|
+
}
|
package/src/store/chain.js
CHANGED
|
@@ -111,6 +111,17 @@ export default class Chain {
|
|
|
111
111
|
return {}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
merge(chain, items) {
|
|
115
|
+
if (this.dead) return
|
|
116
|
+
const cached = this.cache[chain] || {}
|
|
117
|
+
for (const item of items) {
|
|
118
|
+
const node = {...item, chain, full_id: chain + ':' + item.id}
|
|
119
|
+
cached[item.id] = node
|
|
120
|
+
this.db.put('items', node.full_id, node)
|
|
121
|
+
}
|
|
122
|
+
this.cache[chain] = cached
|
|
123
|
+
}
|
|
124
|
+
|
|
114
125
|
del(chain, id) {
|
|
115
126
|
if (this.dead) return
|
|
116
127
|
let inmemItemM = this.cache[chain] || {}
|
|
@@ -127,7 +138,7 @@ export default class Chain {
|
|
|
127
138
|
async reload(chain, api) {
|
|
128
139
|
if (this.dead) return
|
|
129
140
|
|
|
130
|
-
let top = maxIndex(this.
|
|
141
|
+
let top = maxIndex(this.cache[chain] || {}) || {}
|
|
131
142
|
let topindex = top.index
|
|
132
143
|
if (!topindex) topindex = Date.now()
|
|
133
144
|
var lastanchor = ''
|
|
@@ -142,8 +153,17 @@ export default class Chain {
|
|
|
142
153
|
})
|
|
143
154
|
if (newItems.length === 0 || newanchor === lastanchor) break // out of item
|
|
144
155
|
|
|
156
|
+
let minindexitem = minIndex(fetchedItems) || {}
|
|
157
|
+
// try to join old data
|
|
158
|
+
if (minindexitem.index <= topindex) {
|
|
159
|
+
// successfully joined, reuse anchor
|
|
160
|
+
|
|
161
|
+
lastanchor = this.anchorCache[chain] || newanchor
|
|
162
|
+
break
|
|
163
|
+
}
|
|
164
|
+
|
|
145
165
|
// still cannot join
|
|
146
|
-
if (fetchedItems
|
|
166
|
+
if (size(fetchedItems) >= 200) {
|
|
147
167
|
// fetch too much but still cannot merge with old data
|
|
148
168
|
// clear old data and write new data
|
|
149
169
|
|
|
@@ -155,22 +175,15 @@ export default class Chain {
|
|
|
155
175
|
})
|
|
156
176
|
this.db.removes('items', removeids)
|
|
157
177
|
this.cache[chain] = {}
|
|
178
|
+
lastanchor = newanchor
|
|
158
179
|
break
|
|
159
180
|
}
|
|
160
181
|
|
|
161
|
-
let minindexitem = minIndex(fetchedItems) || {}
|
|
162
|
-
// try to join old data
|
|
163
|
-
if (minindexitem.index <= topindex) {
|
|
164
|
-
// successfully joined, reuse anchor
|
|
165
182
|
|
|
166
|
-
lastanchor = this.anchorCache[chain] || newanchor
|
|
167
|
-
break
|
|
168
|
-
}
|
|
169
183
|
lastanchor = newanchor
|
|
170
184
|
}
|
|
171
185
|
|
|
172
|
-
for (
|
|
173
|
-
let item = fetchedItems[Object.keys(fetchedItems)[i]]
|
|
186
|
+
for (const item of Object.values(fetchedItems)) {
|
|
174
187
|
this.db.put('items', item.full_id, item)
|
|
175
188
|
this.cache[item.chain] = this.cache[item.chain] || {}
|
|
176
189
|
this.cache[item.chain][item.id] = item
|
|
@@ -210,7 +223,7 @@ function maxIndex(obj) {
|
|
|
210
223
|
}
|
|
211
224
|
}
|
|
212
225
|
|
|
213
|
-
return obj[
|
|
226
|
+
return obj[maxKey]
|
|
214
227
|
}
|
|
215
228
|
|
|
216
229
|
function map(collection, predicate) {
|