@subiz/widget-v4 4.1.122 → 4.1.124
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/assets/less/form.less +1 -1
- package/assets/less/message.less +7 -0
- package/assets/less/widget_header.less +5 -3
- package/compiled_src/chatbox/Event.js +15 -11
- package/compiled_src/chatbox/TextMessage.js +6 -2
- package/compiled_src/chatbox/Widget.js +4 -6
- package/compiled_src/plugin.js +1 -0
- package/compiled_src/store/api.js +5 -2
- package/compiled_src/store/api_fake.js +0 -1
- package/compiled_src/store/chain.js +228 -0
- package/compiled_src/store/inmem_localstorage.js +33 -0
- package/compiled_src/store/kv.js +86 -0
- package/compiled_src/store/lsdb.js +68 -0
- package/compiled_src/store/store.js +27 -20
- package/dist/132.js +6433 -2574
- package/dist/178.js +406 -0
- package/dist/18a02a9c54e.132.js +1 -0
- package/dist/18a02a9c54e.178.js +1 -0
- package/dist/18a02a9c54e.213.js +1 -0
- package/dist/18a02a9c54e.335.js +1 -0
- package/dist/18a02a9c54e.362.js +1 -0
- package/dist/18a02a9c54e.441.js +1 -0
- package/dist/18a02a9c54e.605.js +1 -0
- package/dist/18a02a9c54e.678.js +1 -0
- package/dist/18a02a9c54e.782.js +1 -0
- package/dist/18a02a9c54e.app.js +1 -0
- package/dist/{19643345ab6.index.html → 18a02a9c54e.index.html} +4 -28
- package/dist/{19643345ab6.library.html → 18a02a9c54e.library.html} +4 -28
- package/dist/18a02a9c54e.library.js +1 -0
- package/dist/{19643345ab6.preview.html → 18a02a9c54e.preview.html} +1 -1
- package/dist/18a02a9c54e.preview.js +1 -0
- package/dist/18a02a9c54e.screenshoot.js +1 -0
- package/dist/18a02a9c54e.webview.js +1 -0
- package/dist/213.js +2780 -0
- package/dist/{74.js → 335.js} +35 -35
- package/dist/362.js +107 -0
- package/dist/441.js +2855 -0
- package/dist/{490.js → 605.js} +82 -82
- package/dist/{997.js → 678.js} +6 -6
- package/dist/782.js +107 -0
- package/dist/HASH +1 -1
- package/dist/assets/img/{subiz-mono.8fbc067.svg → subiz-mono.877e9f5.svg} +1 -1
- package/dist/malicious-app.js +4 -6
- package/dist/screenshoot.html +1 -1
- package/dist/screenshoot.js +4849 -9583
- package/package.json +1 -2
- package/src/chatbox/Event.js +5 -3
- package/src/chatbox/TextMessage.js +6 -2
- package/src/chatbox/Widget.js +3 -4
- package/src/chatbox/main.js +1 -0
- package/src/plugin.js +2 -0
- package/src/store/api.js +5 -2
- package/src/store/api_fake.js +0 -1
- package/src/store/chain.js +255 -0
- package/src/store/inmem_localstorage.js +40 -0
- package/src/store/kv.js +103 -0
- package/src/store/lsdb.js +74 -0
- package/src/store/store.js +27 -22
- package/dist/127.js +0 -273
- package/dist/185.js +0 -1885
- package/dist/19643345ab6.127.js +0 -1
- package/dist/19643345ab6.132.js +0 -1
- package/dist/19643345ab6.185.js +0 -1
- package/dist/19643345ab6.27.js +0 -1
- package/dist/19643345ab6.278.js +0 -1
- package/dist/19643345ab6.459.js +0 -1
- package/dist/19643345ab6.490.js +0 -1
- package/dist/19643345ab6.74.js +0 -1
- package/dist/19643345ab6.997.js +0 -1
- package/dist/19643345ab6.app.js +0 -1
- package/dist/19643345ab6.library.js +0 -1
- package/dist/19643345ab6.preview.js +0 -1
- package/dist/19643345ab6.webview.js +0 -1
- package/dist/27.js +0 -107
- package/dist/278.js +0 -406
- package/dist/459.js +0 -107
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subiz/widget-v4",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.124",
|
|
4
4
|
"main": "./compiled_src/lib.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "npx webpack serve --config webpack.dev.js",
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@subiz/ajax": "^3.0.19",
|
|
13
13
|
"@subiz/flow": "^1.1.0",
|
|
14
|
-
"@subiz/storage": "0.0.8",
|
|
15
14
|
"@subiz/wsclient": "^4.0.28",
|
|
16
15
|
"cookie_js": "1.4.2",
|
|
17
16
|
"core-js": "^3.41.0",
|
package/src/chatbox/Event.js
CHANGED
|
@@ -302,13 +302,15 @@ export default class Event extends Com {
|
|
|
302
302
|
if (this.props.event.mode === 'lead') $sender = <div class='pt-3'></div>
|
|
303
303
|
|
|
304
304
|
let $error = null
|
|
305
|
+
if (ev._error) $dot = null
|
|
305
306
|
if (ev._error && ev.type === 'message_sent') {
|
|
306
307
|
let msg = this.props.t('error_send_message')
|
|
307
308
|
if (ev._error_message) msg = this.props.t(ev._error_message)
|
|
308
309
|
$error = (
|
|
309
|
-
<a class='message_error' src='javascript:;' onClick={
|
|
310
|
-
<div class='message_error__text'>{msg}</div>
|
|
310
|
+
<a class='message_error' src='javascript:;' onClick={this.resendMessage}>
|
|
311
311
|
<div class='message_error__icon' />
|
|
312
|
+
|
|
313
|
+
<div class='message_error__text'>{msg}</div>
|
|
312
314
|
</a>
|
|
313
315
|
)
|
|
314
316
|
}
|
|
@@ -436,7 +438,7 @@ export default class Event extends Com {
|
|
|
436
438
|
return <div class='message-system'>{this.props.t('conversation_state_updated', {state})}</div>
|
|
437
439
|
}
|
|
438
440
|
|
|
439
|
-
resendMessage() {
|
|
441
|
+
resendMessage = () => {
|
|
440
442
|
let cid = sb.convoOfEv(this.props.event.ev)
|
|
441
443
|
const message = sb.get(this.props.event, 'ev.data.message')
|
|
442
444
|
this.props.store.sendConvoEvent(cid, 'message_sent', {message})
|
|
@@ -63,8 +63,10 @@ export default class TextMessage extends Com {
|
|
|
63
63
|
{this.renderTime()}
|
|
64
64
|
</div>
|
|
65
65
|
)
|
|
66
|
+
let msgcls = 'message-user message-text--user'
|
|
67
|
+
if (this.props.ev._error) msgcls += ' message-user--error'
|
|
66
68
|
return (
|
|
67
|
-
<div class=
|
|
69
|
+
<div class={msgcls} title={t}>
|
|
68
70
|
{tags}
|
|
69
71
|
</div>
|
|
70
72
|
)
|
|
@@ -83,8 +85,10 @@ export default class TextMessage extends Com {
|
|
|
83
85
|
</div>
|
|
84
86
|
)
|
|
85
87
|
}
|
|
88
|
+
let msgcls = 'message-user message-text--user'
|
|
89
|
+
if (this.props.ev._error) msgcls += ' message-user--error'
|
|
86
90
|
return (
|
|
87
|
-
<div class=
|
|
91
|
+
<div class={msgcls} title={t}>
|
|
88
92
|
<Block block={block} onLink={this.props.onLink} />
|
|
89
93
|
</div>
|
|
90
94
|
)
|
package/src/chatbox/Widget.js
CHANGED
|
@@ -609,7 +609,7 @@ export default class Widget extends Com {
|
|
|
609
609
|
<div key={'form_' + encode(key)}>
|
|
610
610
|
<label class='label' for={'form_' + encode(key)}>
|
|
611
611
|
{label}
|
|
612
|
-
{field.is_required ? <span class='
|
|
612
|
+
{field.is_required ? <span class='red'>*</span> : ''}
|
|
613
613
|
</label>
|
|
614
614
|
{$field}
|
|
615
615
|
{field.error ? <div class='form-control--valid-input--error'>{field.error}</div> : ''}
|
|
@@ -631,8 +631,8 @@ export default class Widget extends Com {
|
|
|
631
631
|
return (
|
|
632
632
|
<div class='widget-body'>
|
|
633
633
|
<FormMounter key={form._updated} form={form} onMount={this.formMounted} />
|
|
634
|
-
<div class='widget-body--content flex-grow-1 pl-3 pr-3 d-flex flex-column'>
|
|
635
|
-
<div class='mt-
|
|
634
|
+
<div class='widget-body--content flex-grow-1 pl-3 pr-3 pb-2 d-flex flex-column'>
|
|
635
|
+
<div class='mt-2 d-flex flex-column'>
|
|
636
636
|
<div>{title}</div>
|
|
637
637
|
{this.renderFields(form, formName)}
|
|
638
638
|
</div>
|
|
@@ -645,7 +645,6 @@ export default class Widget extends Com {
|
|
|
645
645
|
{submitLabel}
|
|
646
646
|
</button>
|
|
647
647
|
</div>
|
|
648
|
-
<div class='mt-2'></div>
|
|
649
648
|
</div>
|
|
650
649
|
)
|
|
651
650
|
}
|
package/src/chatbox/main.js
CHANGED
|
@@ -70,6 +70,7 @@ export default class Main extends Com {
|
|
|
70
70
|
if (!this.state.cssready) return null
|
|
71
71
|
if (sb.isUserBanned(this.props.store.matchUser())) return null
|
|
72
72
|
if (!this.props.t) return null
|
|
73
|
+
|
|
73
74
|
return (
|
|
74
75
|
<div class={this.props.store.isMobile() ? 'mobile' : 'desktop'}>
|
|
75
76
|
<ChatButton
|
package/src/plugin.js
CHANGED
|
@@ -69,6 +69,8 @@ function Plugin(plugin, store, $container, lib) {
|
|
|
69
69
|
var _render = async () => {
|
|
70
70
|
if (_destroy) return
|
|
71
71
|
if (!$div) return
|
|
72
|
+
if (!this.t) return
|
|
73
|
+
|
|
72
74
|
if (plugin.type === 'chatbox') {
|
|
73
75
|
// if (!Chatbox) Chatbox = (await LoadChatbox()).default
|
|
74
76
|
render(<Chatbox destroy={_destroy} plugin={plugin} store={store} ref={_ref} t={this.t} />, $div)
|
package/src/store/api.js
CHANGED
|
@@ -63,7 +63,11 @@ export default class {
|
|
|
63
63
|
.addQuery('x-user-ref', this.ref)
|
|
64
64
|
.setParser('json')
|
|
65
65
|
.get(`/accounts/${this.accid}/users/${this.ref}`)
|
|
66
|
-
if (
|
|
66
|
+
if (sb.get(outuser, 'body.error.code', '').indexOf('locked_account') != -1) break
|
|
67
|
+
if (sb.get(outuser, 'body.error.code', '').indexOf('user_is_banned') != -1) {
|
|
68
|
+
await sb.sleep(120000) // sleep 2 min, incase of unblock
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
67
71
|
|
|
68
72
|
if (!sb.get(outuser, 'body.id')) {
|
|
69
73
|
let sleeptime = Math.min(2000 + i * 400, 60000)
|
|
@@ -286,7 +290,6 @@ export default class {
|
|
|
286
290
|
|
|
287
291
|
getUser = (_) => this.apireq.get(`/accounts/${this.accid}/users/${this.uid}`)
|
|
288
292
|
getAccountSetting = () => this.apireq.get(`/accounts/${this.accid}/settings`)
|
|
289
|
-
getIsOnline = () => this.apireq.get(`/accounts/${this.accid}/is_online`)
|
|
290
293
|
|
|
291
294
|
sendImpression = (impression) =>
|
|
292
295
|
this.apireq.post(`/accounts/${this.accid}/campaigns/${impression.campaign_id}/impressions`, impression)
|
package/src/store/api_fake.js
CHANGED
|
@@ -86,7 +86,6 @@ export default class {
|
|
|
86
86
|
},
|
|
87
87
|
})
|
|
88
88
|
getAccountSetting = () => this.apireq.get(`/accounts/${this.accid}/settings`)
|
|
89
|
-
getIsOnline = () => this.apireq.get(`/accounts/${this.accid}/is_online`)
|
|
90
89
|
|
|
91
90
|
sendImpression = (impression) => OK200
|
|
92
91
|
sendConversion = (conversion) => OK200
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
const ChainMaxSize = 50
|
|
2
|
+
// NOTE:
|
|
3
|
+
// index must be number and index must be positive
|
|
4
|
+
//
|
|
5
|
+
|
|
6
|
+
export default class Chain {
|
|
7
|
+
constructor(db, schema, inmem) {
|
|
8
|
+
this.schema = schema + '_chain'
|
|
9
|
+
this.dead = false
|
|
10
|
+
this.db = db
|
|
11
|
+
this.anchorCache = {}
|
|
12
|
+
this.cache = {} // note: cache must be continuously from start
|
|
13
|
+
|
|
14
|
+
this.init()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
init() {
|
|
18
|
+
let inmemItemM = {}
|
|
19
|
+
|
|
20
|
+
let allitems = this.db.all('items')
|
|
21
|
+
allitems.map((item) => {
|
|
22
|
+
inmemItemM[item.chain] = inmemItemM[item.chain] || {}
|
|
23
|
+
inmemItemM[item.chain][item.id] = {
|
|
24
|
+
id: item.id,
|
|
25
|
+
chain: item.chain,
|
|
26
|
+
full_id: item.full_id,
|
|
27
|
+
index: item.index,
|
|
28
|
+
value: item.value,
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
map(inmemItemM, (itemM, chain) => {
|
|
33
|
+
if (size(itemM) < ChainMaxSize) return
|
|
34
|
+
let items = orderBy(map(itemM), ['index'], 'desc')
|
|
35
|
+
let removekeys = []
|
|
36
|
+
for (var i = ChainMaxSize; i < items.length; i++) {
|
|
37
|
+
removekeys.push(items[i].full_id)
|
|
38
|
+
delete itemM[items[i].id]
|
|
39
|
+
}
|
|
40
|
+
this.db.removes('items', removekeys)
|
|
41
|
+
inmemItemM[chain] = itemM
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
this.cache = inmemItemM
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
destroy() {
|
|
48
|
+
// clear all data
|
|
49
|
+
this.db.clear('items')
|
|
50
|
+
|
|
51
|
+
this.dead = true
|
|
52
|
+
this.cache = {}
|
|
53
|
+
this.anchorCache = {}
|
|
54
|
+
this.db = undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// item: {id, index, value}
|
|
58
|
+
// note: should only call update when db is synced
|
|
59
|
+
put(chain, id, node_index, value) {
|
|
60
|
+
if (this.dead) return
|
|
61
|
+
|
|
62
|
+
let inmemItemM = this.cache[chain] || {}
|
|
63
|
+
let bottom = minIndex(inmemItemM) || {}
|
|
64
|
+
let bottomIndex = bottom.index || 'z'
|
|
65
|
+
if (size(inmemItemM) == 0) bottomIndex = ''
|
|
66
|
+
|
|
67
|
+
let item = {id, index: node_index, value, chain, full_id: chain + ':' + id}
|
|
68
|
+
delete inmemItemM[item.id]
|
|
69
|
+
|
|
70
|
+
if (bottomIndex > item.index + '') return
|
|
71
|
+
inmemItemM[item.id] = item
|
|
72
|
+
|
|
73
|
+
this.cache[chain] = inmemItemM
|
|
74
|
+
this.db.put('items', item.full_id, item)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
listM(chain) {
|
|
78
|
+
if (this.dead) return {}
|
|
79
|
+
let inmemItemM = this.cache[chain] || {}
|
|
80
|
+
let out = {}
|
|
81
|
+
map(inmemItemM, (item, id) => {
|
|
82
|
+
out[id] = item.value
|
|
83
|
+
})
|
|
84
|
+
return out
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async fetchMore(chain, limit = 20, api) {
|
|
88
|
+
if (this.dead) return {error: 'dead'}
|
|
89
|
+
if (!api) return {error: 'no api'}
|
|
90
|
+
|
|
91
|
+
let anchor = this.anchorCache[chain] || ''
|
|
92
|
+
var [apiItems, newanchor, error] = await api(chain, anchor, limit)
|
|
93
|
+
|
|
94
|
+
if (error) return {error}
|
|
95
|
+
if (this.dead) return {error: 'dead'}
|
|
96
|
+
|
|
97
|
+
let inmemItemM = this.cache[chain] || {}
|
|
98
|
+
|
|
99
|
+
if (apiItems.length === 0) return {end: true}
|
|
100
|
+
|
|
101
|
+
apiItems.forEach((item) => {
|
|
102
|
+
item.chain = chain
|
|
103
|
+
item.full_id = chain + ':' + item.id
|
|
104
|
+
inmemItemM[item.id] = item
|
|
105
|
+
this.db.put('items', item.full_id, item)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// save items fetch from api to the db
|
|
109
|
+
this.anchorCache[chain] = newanchor
|
|
110
|
+
this.cache[chain] = inmemItemM
|
|
111
|
+
return {}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
del(chain, id) {
|
|
115
|
+
if (this.dead) return
|
|
116
|
+
let inmemItemM = this.cache[chain] || {}
|
|
117
|
+
let full_id = chain + ':' + id
|
|
118
|
+
|
|
119
|
+
delete inmemItemM[id]
|
|
120
|
+
this.db.removes('items', [full_id])
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
match(chain, id) {
|
|
124
|
+
return this.listM(chain)[id]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async reload(chain, api) {
|
|
128
|
+
if (this.dead) return
|
|
129
|
+
|
|
130
|
+
let top = maxIndex(this.listM(chain)) || {}
|
|
131
|
+
let topindex = top.index
|
|
132
|
+
if (!topindex) topindex = Date.now()
|
|
133
|
+
var lastanchor = ''
|
|
134
|
+
var fetchedItems = {}
|
|
135
|
+
for (;;) {
|
|
136
|
+
if (this.dead) return
|
|
137
|
+
let [newItems, newanchor, error] = await api(chain, lastanchor, 50)
|
|
138
|
+
if (error) return
|
|
139
|
+
if (this.dead) return
|
|
140
|
+
newItems.map((item) => {
|
|
141
|
+
fetchedItems[item.id] = Object.assign({}, item, {chain, full_id: chain + ':' + item.id})
|
|
142
|
+
})
|
|
143
|
+
if (newItems.length === 0 || newanchor === lastanchor) break // out of item
|
|
144
|
+
|
|
145
|
+
// still cannot join
|
|
146
|
+
if (fetchedItems.length >= 200) {
|
|
147
|
+
// fetch too much but still cannot merge with old data
|
|
148
|
+
// clear old data and write new data
|
|
149
|
+
|
|
150
|
+
let inmemItemM = this.cache[chain] || {}
|
|
151
|
+
let removeids = []
|
|
152
|
+
map(inmemItemM, (item) => {
|
|
153
|
+
if (item.chain !== chain) return
|
|
154
|
+
removeids.push(item.full_id)
|
|
155
|
+
})
|
|
156
|
+
this.db.removes('items', removeids)
|
|
157
|
+
this.cache[chain] = {}
|
|
158
|
+
break
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let minindexitem = minIndex(fetchedItems) || {}
|
|
162
|
+
// try to join old data
|
|
163
|
+
if (minindexitem.index <= topindex) {
|
|
164
|
+
// successfully joined, reuse anchor
|
|
165
|
+
|
|
166
|
+
lastanchor = this.anchorCache[chain] || newanchor
|
|
167
|
+
break
|
|
168
|
+
}
|
|
169
|
+
lastanchor = newanchor
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (var i = 0; i < Object.keys(fetchedItems).length; i++) {
|
|
173
|
+
let item = fetchedItems[Object.keys(fetchedItems)[i]]
|
|
174
|
+
this.db.put('items', item.full_id, item)
|
|
175
|
+
this.cache[item.chain] = this.cache[item.chain] || {}
|
|
176
|
+
this.cache[item.chain][item.id] = item
|
|
177
|
+
}
|
|
178
|
+
this.anchorCache[chain] = lastanchor
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function minIndex(obj) {
|
|
183
|
+
if (!obj) return
|
|
184
|
+
let keys = Object.keys(obj)
|
|
185
|
+
if (!keys.length) return
|
|
186
|
+
let min = Infinity
|
|
187
|
+
let minKey = Object.keys(obj)[0]
|
|
188
|
+
for (var i = 0; i < keys.length; i++) {
|
|
189
|
+
let index = (obj[keys[i]] && obj[keys[i]].index) || Infinity
|
|
190
|
+
if (index < min) {
|
|
191
|
+
min = index
|
|
192
|
+
minKey = keys[i]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return obj[minKey]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function maxIndex(obj) {
|
|
200
|
+
if (!obj) return
|
|
201
|
+
let keys = Object.keys(obj)
|
|
202
|
+
if (!keys.length) return
|
|
203
|
+
let max = -Infinity
|
|
204
|
+
let maxKey = Object.keys(obj)[0]
|
|
205
|
+
for (var i = 0; i < keys.length; i++) {
|
|
206
|
+
let index = (obj[keys[i]] && obj[keys[i]].index) || -Infinity
|
|
207
|
+
if (index > max) {
|
|
208
|
+
max = index
|
|
209
|
+
maxKey = keys[i]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return obj[keys[maxKey]]
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function map(collection, predicate) {
|
|
217
|
+
if (!collection) return []
|
|
218
|
+
if (!predicate) return collection
|
|
219
|
+
|
|
220
|
+
var out = []
|
|
221
|
+
if (Array.isArray(collection)) {
|
|
222
|
+
for (let i = 0; i < collection.length; i++) {
|
|
223
|
+
out.push(predicate(collection[i], i))
|
|
224
|
+
}
|
|
225
|
+
return out
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (typeof collection === 'object') {
|
|
229
|
+
var keys = Object.keys(collection)
|
|
230
|
+
for (let i = 0; i < keys.length; i++) {
|
|
231
|
+
var k = keys[i]
|
|
232
|
+
out.push(predicate(collection[k], k))
|
|
233
|
+
}
|
|
234
|
+
return out
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return out
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function size(o) {
|
|
241
|
+
if (!o) return 0
|
|
242
|
+
if (o.length !== undefined) return o.length
|
|
243
|
+
return Object.keys(o).length
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function orderBy(a, k, d) {
|
|
247
|
+
var clone = []
|
|
248
|
+
map(a, (v) => clone.push(v))
|
|
249
|
+
clone.sort((i, j) => {
|
|
250
|
+
if (i[k] === j[k]) return 0
|
|
251
|
+
var r = i[k] < j[k] ? -1 : 1
|
|
252
|
+
return d === 'desc' ? -r : r
|
|
253
|
+
})
|
|
254
|
+
return clone
|
|
255
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// this file implements localstorage in memory
|
|
2
|
+
var localStorageMemory = {}
|
|
3
|
+
var cache = {}
|
|
4
|
+
|
|
5
|
+
localStorageMemory.length = 0
|
|
6
|
+
|
|
7
|
+
localStorageMemory.getItem = function (key) {
|
|
8
|
+
if (key in cache) return cache[key]
|
|
9
|
+
return null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
localStorageMemory.setItem = function (key, value) {
|
|
13
|
+
if (typeof value === 'undefined') {
|
|
14
|
+
localStorageMemory.removeItem(key)
|
|
15
|
+
} else {
|
|
16
|
+
if (!Object.prototype.hasOwnProperty.call(cache, key)) {
|
|
17
|
+
localStorageMemory.length++
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
cache[key] = value
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
localStorageMemory.removeItem = function (key) {
|
|
25
|
+
if (Object.prototype.hasOwnProperty.call(cache, key)) {
|
|
26
|
+
delete cache[key]
|
|
27
|
+
localStorageMemory.length--
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
localStorageMemory.key = function (index) {
|
|
32
|
+
return Object.keys(cache)[index] || null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
localStorageMemory.clear = function () {
|
|
36
|
+
cache = {}
|
|
37
|
+
localStorageMemory.length = 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = localStorageMemory
|
package/src/store/kv.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
let KvMaxSize = 500
|
|
2
|
+
// A key-value database with a cache layer
|
|
3
|
+
export default class {
|
|
4
|
+
constructor (db, schema) {
|
|
5
|
+
this.name = schema
|
|
6
|
+
schema += '_kv'
|
|
7
|
+
this.dead = false
|
|
8
|
+
this.db = db
|
|
9
|
+
this.cache = {}
|
|
10
|
+
|
|
11
|
+
this.initialized = false
|
|
12
|
+
this.init()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
refreshCache () {
|
|
16
|
+
if (this.dead) return
|
|
17
|
+
let inmemItemM = {}
|
|
18
|
+
let allitems = this.db.all('items')
|
|
19
|
+
allitems.forEach(function(item) {
|
|
20
|
+
inmemItemM[item.key] = item.value
|
|
21
|
+
})
|
|
22
|
+
this.cache = inmemItemM
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
init () {
|
|
26
|
+
if (this.initialized) return
|
|
27
|
+
let inmemItemM = {}
|
|
28
|
+
|
|
29
|
+
console.time('KV' + this.name)
|
|
30
|
+
let allitems = this.db.all('items')
|
|
31
|
+
allitems.forEach(function(item) {
|
|
32
|
+
inmemItemM[item.key] = item.value
|
|
33
|
+
})
|
|
34
|
+
this.cache = inmemItemM
|
|
35
|
+
|
|
36
|
+
// If the cache is full. Randomly remove half of the cache. credit to thanos
|
|
37
|
+
// this is so simple since we don't need to keep track of last modified time
|
|
38
|
+
// for each key. Implementing a LRU cache won't add much value.
|
|
39
|
+
let allkeys = Object.keys(this.cache)
|
|
40
|
+
if (allkeys.length > KvMaxSize) {
|
|
41
|
+
let liveChance = KvMaxSize / 2 / allkeys.lenth
|
|
42
|
+
|
|
43
|
+
let removekeys = []
|
|
44
|
+
for (var i = 0; i < KvMaxSize; i++) {
|
|
45
|
+
let live = Math.random() <= liveChance
|
|
46
|
+
if (live) continue
|
|
47
|
+
|
|
48
|
+
let key = allkeys[i]
|
|
49
|
+
delete this.cache[key]
|
|
50
|
+
removekeys.push(key)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.db.removes('items', [removekeys])
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.initialized = true
|
|
58
|
+
console.timeEnd('KV' + this.name)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
all () {
|
|
62
|
+
if (this.dead) return []
|
|
63
|
+
if (!this.initialized) return []
|
|
64
|
+
return this.cache
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
match (key) {
|
|
68
|
+
if (this.dead || !this.initialized) return
|
|
69
|
+
if (!key) return
|
|
70
|
+
return this.cache[key]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// keys could be a string or array
|
|
74
|
+
// put('1', {id: '1', name: 'thanh'})
|
|
75
|
+
// put({id: '1', name: 'thanh'}, 'id')
|
|
76
|
+
// put([{id: '1', name: 'thanh'},{id: '2', name: 'van'}], 'id')
|
|
77
|
+
put (key, value) {
|
|
78
|
+
if (!key) return
|
|
79
|
+
if (this.dead) return []
|
|
80
|
+
if (!this.initialized) throw new Error('uninitialized')
|
|
81
|
+
// if multiple key
|
|
82
|
+
this.cache[key] = value
|
|
83
|
+
this.db.put('items', key, { key, value })
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// delete an record in a schema
|
|
87
|
+
del (key) {
|
|
88
|
+
if (this.dead) return
|
|
89
|
+
if (!this.initialized) throw new Error('uninitialized')
|
|
90
|
+
delete this.cache[key]
|
|
91
|
+
this.db.removes('items', [key])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
destroy () {
|
|
95
|
+
// clear all data
|
|
96
|
+
this.db.clear('items')
|
|
97
|
+
|
|
98
|
+
this.dead = true
|
|
99
|
+
this.db = undefined
|
|
100
|
+
this.cache = undefined
|
|
101
|
+
this.initialized = false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// like db but implement using localstorage
|
|
2
|
+
let memlocalStorage = require('./inmem_localstorage.js')
|
|
3
|
+
import sb from '../util/index.js'
|
|
4
|
+
|
|
5
|
+
let requestIdleCallback = typeof window !== 'undefined' ? window.requestIdleCallback : null
|
|
6
|
+
if (!requestIdleCallback) requestIdleCallback = (cb) => cb()
|
|
7
|
+
|
|
8
|
+
const LIMIT = 80
|
|
9
|
+
|
|
10
|
+
export default class {
|
|
11
|
+
constructor(schema, inmem) {
|
|
12
|
+
this.localStorage = memlocalStorage
|
|
13
|
+
this.inmem = inmem
|
|
14
|
+
this.allkeys = {}
|
|
15
|
+
if (!inmem) {
|
|
16
|
+
try {
|
|
17
|
+
window.localStorage.setItem('sbzstore1029', '?')
|
|
18
|
+
window.localStorage.removeItem('sbzstore1029')
|
|
19
|
+
this.localStorage = window.localStorage
|
|
20
|
+
} catch (e) {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.schema = schema
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
all(table) {
|
|
27
|
+
let items = []
|
|
28
|
+
for (var i = 0; i < this.localStorage.length; i++) {
|
|
29
|
+
let key = this.localStorage.key(i)
|
|
30
|
+
if (!key.startsWith(this.schema + '#' + table + '#')) continue
|
|
31
|
+
this.allkeys[key] = true
|
|
32
|
+
if (this.inmem)
|
|
33
|
+
items.push(this.localStorage.getItem(key)) // fast path
|
|
34
|
+
else items.push(sb.parseJSON(this.localStorage.getItem(key)))
|
|
35
|
+
}
|
|
36
|
+
return items
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
removes(table, keys) {
|
|
40
|
+
for (var i = 0; i < keys.length; i++) {
|
|
41
|
+
delete this.allkeys[this.schema + '#' + table + '#' + keys[i]]
|
|
42
|
+
this.localStorage.removeItem(this.schema + '#' + table + '#' + keys[i])
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
clear(table) {
|
|
47
|
+
for (var i = 0; i < this.localStorage.length; i++) {
|
|
48
|
+
let key = this.localStorage.key(i)
|
|
49
|
+
if (!key.startsWith(this.schema + '#' + table + '#')) continue
|
|
50
|
+
this.localStorage.removeItem(key)
|
|
51
|
+
}
|
|
52
|
+
this.allkeys = {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// write a (key,value) pair to the table
|
|
56
|
+
// if the localstorage is already full, it would try to free by delete the whole table!
|
|
57
|
+
put(table, key, value) {
|
|
58
|
+
this.allkeys[this.schema + '#' + table + '#' + key] = true
|
|
59
|
+
if (this.inmem) return this.localStorage.setItem(this.schema + '#' + table + '#' + key, value) // fast path
|
|
60
|
+
if (Object.keys(this.allkeys).length > LIMIT) this.clear(table)
|
|
61
|
+
requestIdleCallback(() => {
|
|
62
|
+
try {
|
|
63
|
+
this.localStorage.setItem(this.schema + '#' + table + '#' + key, toString(value))
|
|
64
|
+
} catch (e) {
|
|
65
|
+
this.clear(table)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function toString(obj) {
|
|
72
|
+
if (obj === undefined) return 'null'
|
|
73
|
+
return JSON.stringify(obj)
|
|
74
|
+
}
|