@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.
Files changed (76) hide show
  1. package/assets/less/form.less +1 -1
  2. package/assets/less/message.less +7 -0
  3. package/assets/less/widget_header.less +5 -3
  4. package/compiled_src/chatbox/Event.js +15 -11
  5. package/compiled_src/chatbox/TextMessage.js +6 -2
  6. package/compiled_src/chatbox/Widget.js +4 -6
  7. package/compiled_src/plugin.js +1 -0
  8. package/compiled_src/store/api.js +5 -2
  9. package/compiled_src/store/api_fake.js +0 -1
  10. package/compiled_src/store/chain.js +228 -0
  11. package/compiled_src/store/inmem_localstorage.js +33 -0
  12. package/compiled_src/store/kv.js +86 -0
  13. package/compiled_src/store/lsdb.js +68 -0
  14. package/compiled_src/store/store.js +27 -20
  15. package/dist/132.js +6433 -2574
  16. package/dist/178.js +406 -0
  17. package/dist/18a02a9c54e.132.js +1 -0
  18. package/dist/18a02a9c54e.178.js +1 -0
  19. package/dist/18a02a9c54e.213.js +1 -0
  20. package/dist/18a02a9c54e.335.js +1 -0
  21. package/dist/18a02a9c54e.362.js +1 -0
  22. package/dist/18a02a9c54e.441.js +1 -0
  23. package/dist/18a02a9c54e.605.js +1 -0
  24. package/dist/18a02a9c54e.678.js +1 -0
  25. package/dist/18a02a9c54e.782.js +1 -0
  26. package/dist/18a02a9c54e.app.js +1 -0
  27. package/dist/{19643345ab6.index.html → 18a02a9c54e.index.html} +4 -28
  28. package/dist/{19643345ab6.library.html → 18a02a9c54e.library.html} +4 -28
  29. package/dist/18a02a9c54e.library.js +1 -0
  30. package/dist/{19643345ab6.preview.html → 18a02a9c54e.preview.html} +1 -1
  31. package/dist/18a02a9c54e.preview.js +1 -0
  32. package/dist/18a02a9c54e.screenshoot.js +1 -0
  33. package/dist/18a02a9c54e.webview.js +1 -0
  34. package/dist/213.js +2780 -0
  35. package/dist/{74.js → 335.js} +35 -35
  36. package/dist/362.js +107 -0
  37. package/dist/441.js +2855 -0
  38. package/dist/{490.js → 605.js} +82 -82
  39. package/dist/{997.js → 678.js} +6 -6
  40. package/dist/782.js +107 -0
  41. package/dist/HASH +1 -1
  42. package/dist/assets/img/{subiz-mono.8fbc067.svg → subiz-mono.877e9f5.svg} +1 -1
  43. package/dist/malicious-app.js +4 -6
  44. package/dist/screenshoot.html +1 -1
  45. package/dist/screenshoot.js +4849 -9583
  46. package/package.json +1 -2
  47. package/src/chatbox/Event.js +5 -3
  48. package/src/chatbox/TextMessage.js +6 -2
  49. package/src/chatbox/Widget.js +3 -4
  50. package/src/chatbox/main.js +1 -0
  51. package/src/plugin.js +2 -0
  52. package/src/store/api.js +5 -2
  53. package/src/store/api_fake.js +0 -1
  54. package/src/store/chain.js +255 -0
  55. package/src/store/inmem_localstorage.js +40 -0
  56. package/src/store/kv.js +103 -0
  57. package/src/store/lsdb.js +74 -0
  58. package/src/store/store.js +27 -22
  59. package/dist/127.js +0 -273
  60. package/dist/185.js +0 -1885
  61. package/dist/19643345ab6.127.js +0 -1
  62. package/dist/19643345ab6.132.js +0 -1
  63. package/dist/19643345ab6.185.js +0 -1
  64. package/dist/19643345ab6.27.js +0 -1
  65. package/dist/19643345ab6.278.js +0 -1
  66. package/dist/19643345ab6.459.js +0 -1
  67. package/dist/19643345ab6.490.js +0 -1
  68. package/dist/19643345ab6.74.js +0 -1
  69. package/dist/19643345ab6.997.js +0 -1
  70. package/dist/19643345ab6.app.js +0 -1
  71. package/dist/19643345ab6.library.js +0 -1
  72. package/dist/19643345ab6.preview.js +0 -1
  73. package/dist/19643345ab6.webview.js +0 -1
  74. package/dist/27.js +0 -107
  75. package/dist/278.js +0 -406
  76. 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.122",
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",
@@ -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={(e) => this.resendMessage()}>
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
+ &nbsp;
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='message-user message-text--user' title={t}>
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='message-user message-text--user' title={t}>
91
+ <div class={msgcls} title={t}>
88
92
  <Block block={block} onLink={this.props.onLink} />
89
93
  </div>
90
94
  )
@@ -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='ml-1 red'>*</span> : ''}
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-3 d-flex flex-column'>
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
  }
@@ -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 (outuser.error && sb.get(outuser, 'body.error.code', '').indexOf('locked_account') != -1) break
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)
@@ -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
@@ -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
+ }