@subiz/widget-v4 4.1.216 → 4.1.218
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/message.less +83 -0
- package/compiled_src/chatbox/AudioAttachments.js +5 -0
- package/compiled_src/chatbox/Event.js +9 -2
- package/compiled_src/chatbox/FileAttachments.js +9 -3
- package/compiled_src/chatbox/ImageAttachments.js +16 -6
- package/compiled_src/chatbox/UploadProgress.js +44 -0
- package/compiled_src/chatbox/VideoAttachments.js +5 -0
- package/compiled_src/store/api.js +77 -78
- package/compiled_src/store/api_fake.js +1 -1
- package/compiled_src/store/attachments.js +5 -1
- package/compiled_src/store/store.js +21 -3
- package/dist/{1a12ecfb6db.app.js → 1a12ed33347.app.js} +1 -1
- package/dist/{1a12ecfb6db.chat.html → 1a12ed33347.chat.html} +1 -1
- package/dist/{1a12ecfb6db.chat.js → 1a12ed33347.chat.js} +1 -1
- package/dist/{1a12ecfb6db.form.html → 1a12ed33347.form.html} +1 -1
- package/dist/{1a12ecfb6db.form.js → 1a12ed33347.form.js} +1 -1
- package/dist/{1a12ecfb6db.index.html → 1a12ed33347.index.html} +1 -1
- package/dist/{1a12ecfb6db.library.html → 1a12ed33347.library.html} +1 -1
- package/dist/{1a12ecfb6db.library.js → 1a12ed33347.library.js} +1 -1
- package/dist/{1a12ecfb6db.preview.html → 1a12ed33347.preview.html} +1 -1
- package/dist/{1a12ecfb6db.preview.js → 1a12ed33347.preview.js} +1 -1
- package/dist/{1a12ecfb6db.webview.js → 1a12ed33347.webview.js} +1 -1
- package/dist/HASH +1 -1
- package/package.json +13 -12
- package/src/chatbox/AudioAttachments.js +2 -0
- package/src/chatbox/Event.js +8 -4
- package/src/chatbox/FileAttachments.js +5 -2
- package/src/chatbox/ImageAttachments.js +6 -2
- package/src/chatbox/UploadProgress.js +21 -0
- package/src/chatbox/VideoAttachments.js +2 -0
- package/src/store/api.js +66 -81
- package/src/store/api_fake.js +1 -1
- package/src/store/attachments.js +5 -1
- package/src/store/store.js +10 -3
- /package/dist/{1a12ecfb6db.127.js → 1a12ed33347.127.js} +0 -0
- /package/dist/{1a12ecfb6db.132.js → 1a12ed33347.132.js} +0 -0
- /package/dist/{1a12ecfb6db.185.js → 1a12ed33347.185.js} +0 -0
- /package/dist/{1a12ecfb6db.27.js → 1a12ed33347.27.js} +0 -0
- /package/dist/{1a12ecfb6db.278.js → 1a12ed33347.278.js} +0 -0
- /package/dist/{1a12ecfb6db.339.js → 1a12ed33347.339.js} +0 -0
- /package/dist/{1a12ecfb6db.459.js → 1a12ed33347.459.js} +0 -0
- /package/dist/{1a12ecfb6db.490.js → 1a12ed33347.490.js} +0 -0
package/dist/HASH
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1a12ed33347
|
package/package.json
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subiz/widget-v4",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.218",
|
|
4
4
|
"main": "./compiled_src/lib.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "npx webpack serve --config webpack.dev.js",
|
|
7
7
|
"build": "npm run lib && rm -rf dist && webpack --config webpack.screenshoot.js && webpack --config webpack.prod.js",
|
|
8
8
|
"report": "npx webpack --config webpack.report.js",
|
|
9
9
|
"lib": "babel src --out-dir compiled_src --ignore \"**/*.test.js,**/*.test2.js\"",
|
|
10
|
-
"test": "node test.cjs"
|
|
11
|
-
"lint": "eslint src",
|
|
12
|
-
"check": "npm run lint && npm test",
|
|
13
|
-
"prepack": "npm run check && npm run lib"
|
|
10
|
+
"test": "node test.cjs"
|
|
14
11
|
},
|
|
15
12
|
"dependencies": {
|
|
16
|
-
"@subiz/ajax": "^3.0.
|
|
13
|
+
"@subiz/ajax": "^3.0.23",
|
|
17
14
|
"@subiz/flow": "^1.1.0",
|
|
18
15
|
"@subiz/wsclient": "^4.0.36",
|
|
19
16
|
"cookie_js": "1.4.2",
|
|
@@ -32,15 +29,19 @@
|
|
|
32
29
|
"@babel/preset-env": "^7.29.7",
|
|
33
30
|
"babel-loader": "^10.1.1",
|
|
34
31
|
"babel-plugin-syntax-jsx": "^6.18.0",
|
|
35
|
-
"eslint": "^10.12.0",
|
|
36
32
|
"globals": "^17.13.0",
|
|
37
33
|
"html-webpack-plugin": "^5.6.8",
|
|
38
|
-
"jsdom": "^30.1.
|
|
39
|
-
"puppeteer": "^25.
|
|
40
|
-
"tape": "^5.10.
|
|
41
|
-
"webpack": "^5.
|
|
34
|
+
"jsdom": "^30.1.3",
|
|
35
|
+
"puppeteer": "^25.14.0",
|
|
36
|
+
"tape": "^5.10.3",
|
|
37
|
+
"webpack": "^5.112.0",
|
|
42
38
|
"webpack-bundle-analyzer": "^5.4.0",
|
|
43
39
|
"webpack-cli": "^7.2.3",
|
|
44
|
-
"webpack-dev-server": "^6.
|
|
40
|
+
"webpack-dev-server": "^6.1.0"
|
|
41
|
+
},
|
|
42
|
+
"allowScripts": {
|
|
43
|
+
"puppeteer@25.14.0": true,
|
|
44
|
+
"fsevents@2.3.3": true,
|
|
45
|
+
"core-js@3.50.0": true
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {UploadingFile} from './UploadProgress.js'
|
|
1
2
|
import sb from '../util/index.js'
|
|
2
3
|
import {h} from 'preact'
|
|
3
4
|
|
|
@@ -12,6 +13,7 @@ export default function AudioAttachments(props) {
|
|
|
12
13
|
if (audios.length === 0) return null
|
|
13
14
|
|
|
14
15
|
var $audios = sb.map(audios, (audio, index) => {
|
|
16
|
+
if (props.sending) return <UploadingFile key={index} attachment={audio} />
|
|
15
17
|
return (
|
|
16
18
|
<div key={index} class={`audio-attachment audio-attachment--${byType}`}>
|
|
17
19
|
<audio class='audio-attachment__audio' controls>
|
package/src/chatbox/Event.js
CHANGED
|
@@ -362,6 +362,10 @@ export default class Event extends Com {
|
|
|
362
362
|
$dot = <div class='sending_dot sending_dot__hide'></div>
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
const hasFile = sb.find(attachments, att => att.type === 'file' || att._file)
|
|
366
|
+
const sendingAttachment = !ev._error && ev.source_event_id === ev.id
|
|
367
|
+
if (hasFile) $dot = null
|
|
368
|
+
|
|
365
369
|
let $sender = null
|
|
366
370
|
if (this.props.event.mode === 'lead' && sb.get(ev, 'by.type') !== 'user') $sender = <div class='pt-3'></div>
|
|
367
371
|
|
|
@@ -392,10 +396,10 @@ export default class Event extends Com {
|
|
|
392
396
|
refresh={this.state.refresh}
|
|
393
397
|
onLink={this.props.onLink}
|
|
394
398
|
/>
|
|
395
|
-
<ImageAttachments store={this.props.store} ev={ev} ref={this.imageAttachmentRef} />
|
|
396
|
-
<AudioAttachments store={this.props.store} ev={ev} />
|
|
397
|
-
<VideoAttachments store={this.props.store} ev={ev} />
|
|
398
|
-
<FileAttachments store={this.props.store} ev={ev} ref={this.fileAttachmentRef} />
|
|
399
|
+
<ImageAttachments store={this.props.store} ev={ev} sending={sendingAttachment} ref={this.imageAttachmentRef} />
|
|
400
|
+
<AudioAttachments store={this.props.store} ev={ev} sending={sendingAttachment} />
|
|
401
|
+
<VideoAttachments store={this.props.store} ev={ev} sending={sendingAttachment} />
|
|
402
|
+
<FileAttachments store={this.props.store} ev={ev} sending={sendingAttachment} ref={this.fileAttachmentRef} />
|
|
399
403
|
<ButtonAttachments store={this.props.store} ev={ev} t={this.props.t} />
|
|
400
404
|
<FormAttachment store={this.props.store} t={this.props.t} ev={ev} />
|
|
401
405
|
<ProductAttachments t={this.props.t} store={this.props.store} ev={ev} />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import UploadProgress from './UploadProgress.js'
|
|
1
2
|
import sb from '../util/index.js'
|
|
2
3
|
import {Com} from '../common.js'
|
|
3
4
|
import {h} from 'preact'
|
|
@@ -33,14 +34,16 @@ export default class FileAttachments extends Com {
|
|
|
33
34
|
|
|
34
35
|
let $files = sb.map(files, (file, index) => {
|
|
35
36
|
let cls = `file-attachment file-attachment--${byType}`
|
|
37
|
+
if (this.props.sending) cls += ' file-attachment--sending'
|
|
36
38
|
if (file._true_idx === this.state.focusedIdx) cls += ` just-focused`
|
|
37
39
|
|
|
38
40
|
return (
|
|
39
|
-
<div key={index} class={cls}>
|
|
40
|
-
<a class='file-attachment--link' href={this.props.store.replaceFileUrl(file.url)} target='_blank'>
|
|
41
|
+
<div key={index} class={cls} aria-busy={!!this.props.sending}>
|
|
42
|
+
<a class='file-attachment--link' href={this.props.sending ? undefined : this.props.store.replaceFileUrl(file.url)} target='_blank'>
|
|
41
43
|
<span class='file-attachment--download mr-2'></span>
|
|
42
44
|
<span class='file-attachment--filename'>{file.name}</span>
|
|
43
45
|
</a>
|
|
46
|
+
{this.props.sending && <UploadProgress attachment={file} compact />}
|
|
44
47
|
</div>
|
|
45
48
|
)
|
|
46
49
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import UploadProgress from './UploadProgress.js'
|
|
1
2
|
import sb from '../util/index.js'
|
|
2
3
|
import {Com} from '../common.js'
|
|
3
4
|
import {h, createRef} from 'preact'
|
|
@@ -82,17 +83,19 @@ export default class ImageAttachment extends Com {
|
|
|
82
83
|
|
|
83
84
|
let cls = `image-attachment image-attachment--${byType}`
|
|
84
85
|
if (image._true_idx === this.state.focusedIdx) cls += ' just-focused'
|
|
86
|
+
const $sending = this.props.sending ? <span class='image-attachment__sending'><UploadProgress attachment={image} /></span> : null
|
|
85
87
|
|
|
86
88
|
if (!image.width || !image.height) {
|
|
87
89
|
return (
|
|
88
90
|
<div class={cls}>
|
|
89
|
-
<a href={this.props.store.replaceFileUrl(image.url)} target='_blank'>
|
|
91
|
+
<a class='image-attachment__preview' href={this.props.sending ? undefined : this.props.store.replaceFileUrl(image.url)} target='_blank' aria-busy={!!this.props.sending}>
|
|
90
92
|
<img
|
|
91
93
|
alt
|
|
92
94
|
class='image-attachment__img'
|
|
93
95
|
src={this.props.store.replaceFileUrl(image.thumbnail_url || image.url)}
|
|
94
96
|
onload={(e) => this.props.onLoad && this.props.onLoad()}
|
|
95
97
|
/>
|
|
98
|
+
{$sending}
|
|
96
99
|
</a>
|
|
97
100
|
</div>
|
|
98
101
|
)
|
|
@@ -108,7 +111,7 @@ export default class ImageAttachment extends Com {
|
|
|
108
111
|
|
|
109
112
|
return (
|
|
110
113
|
<div class={cls}>
|
|
111
|
-
<a href={trueUrl} target='_blank'>
|
|
114
|
+
<a class='image-attachment__preview' href={this.props.sending ? undefined : trueUrl} target='_blank' aria-busy={!!this.props.sending}>
|
|
112
115
|
<img
|
|
113
116
|
alt
|
|
114
117
|
ref={(el) => {
|
|
@@ -123,6 +126,7 @@ export default class ImageAttachment extends Com {
|
|
|
123
126
|
onload={(e) => this.props.onLoad && this.props.onLoad()}
|
|
124
127
|
onerror={(e) => this.onErrorImage(index)}
|
|
125
128
|
/>
|
|
129
|
+
{$sending}
|
|
126
130
|
</a>
|
|
127
131
|
</div>
|
|
128
132
|
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {h} from 'preact'
|
|
2
|
+
|
|
3
|
+
export default function UploadProgress({attachment, compact = false}) {
|
|
4
|
+
const progress = attachment._upload?.putDone || attachment._upload?.file ? 100 : (attachment._upload?.progress ?? 0)
|
|
5
|
+
return <span class={'image-attachment__progress' + (compact ? ' file-attachment__progress' : '')}>
|
|
6
|
+
<span class='image-attachment__progress-label'>{progress}%</span>
|
|
7
|
+
<span class='image-attachment__progress-track' role='progressbar' aria-label={attachment.name || 'Uploading file'} aria-valuemin={0} aria-valuemax={100} aria-valuenow={progress}>
|
|
8
|
+
<span class='image-attachment__progress-fill' ref={el => {
|
|
9
|
+
// Widget styles reset all properties with !important.
|
|
10
|
+
if (el) el.style.setProperty('width', `${progress}%`, 'important')
|
|
11
|
+
}} />
|
|
12
|
+
</span>
|
|
13
|
+
</span>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function UploadingFile({attachment}) {
|
|
17
|
+
return <div class='file-attachment file-attachment--user file-attachment--sending' aria-busy='true'>
|
|
18
|
+
<span class='file-attachment--filename'>{attachment.name}</span>
|
|
19
|
+
<UploadProgress attachment={attachment} compact />
|
|
20
|
+
</div>
|
|
21
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {UploadingFile} from './UploadProgress.js'
|
|
1
2
|
import sb from '../util/index.js'
|
|
2
3
|
import {Com} from '../common.js'
|
|
3
4
|
import {h} from 'preact'
|
|
@@ -19,6 +20,7 @@ export default class VideoAttachments extends Com {
|
|
|
19
20
|
if (videos.length === 0) return null
|
|
20
21
|
|
|
21
22
|
var $videos = sb.map(videos, (video, index) => {
|
|
23
|
+
if (this.props.sending) return <UploadingFile key={index} attachment={video} />
|
|
22
24
|
return (
|
|
23
25
|
<div key={index} class={`video-attachment video-attachment--${byType}`}>
|
|
24
26
|
<video
|
package/src/store/api.js
CHANGED
|
@@ -309,91 +309,76 @@ export default class {
|
|
|
309
309
|
})
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
size: file.size,
|
|
323
|
-
// server reads 'content-type' (not 'type') and signs it into the presigned URL,
|
|
324
|
-
// so the PUT below must send the exact same Content-Type value
|
|
325
|
-
'content-type': file.type,
|
|
326
|
-
})
|
|
327
|
-
.post('/files')
|
|
328
|
-
if (error) return {body, error}
|
|
329
|
-
|
|
330
|
-
let signedurl = body.signed_url
|
|
331
|
-
let fileid = body.id
|
|
332
|
-
|
|
333
|
-
// Step 2: Upload the file to the signed_url
|
|
334
|
-
let err = await new Promise((resolve) => {
|
|
335
|
-
const xhr = new sb.XMLHttpRequest()
|
|
336
|
-
xhr.open('PUT', signedurl, true)
|
|
337
|
-
xhr.setRequestHeader('Content-Type', file.type || '')
|
|
338
|
-
xhr.onload = function () {
|
|
339
|
-
if (xhr.status === 200) {
|
|
340
|
-
resolve()
|
|
341
|
-
} else {
|
|
342
|
-
resolve(new Error(`Upload failed: ${xhr.statusText}`))
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
xhr.onerror = function () {
|
|
347
|
-
resolve(new Error('Network error during upload.'))
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
xhr.send(file)
|
|
351
|
-
})
|
|
352
|
-
if (err) return {error: err}
|
|
353
|
-
|
|
354
|
-
// Step 3: Notify server of upload completion
|
|
355
|
-
var {body, error} = await this.apireq
|
|
356
|
-
.setBaseUrl('https://api5.subiz.com.vn/4.1')
|
|
357
|
-
.setQuery({'account-id': this.accid})
|
|
358
|
-
.post(`/files/${fileid}/uploaded`)
|
|
359
|
-
if (error) return {error}
|
|
360
|
-
return {url: sb.get(body, 'file.url', '')}
|
|
312
|
+
attachmentRequest = () => this.apireq
|
|
313
|
+
.setBaseUrl(new URL('/4.1', this.ApiURL).href)
|
|
314
|
+
.addQuery('x-account-id', this.accid)
|
|
315
|
+
|
|
316
|
+
attachmentJSON = async (path, body, method = 'post') => {
|
|
317
|
+
const result = await this.attachmentRequest()[method](path, body)
|
|
318
|
+
if (result.error || result.body?.error || result.code < 200 || result.code >= 300) {
|
|
319
|
+
throw Object.assign(new Error('Attachment request failed'), {code: result.body?.error?.code, response: result})
|
|
320
|
+
}
|
|
321
|
+
return result.body
|
|
361
322
|
}
|
|
362
323
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
.
|
|
375
|
-
.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
324
|
+
uploadAttachment = async (file, state = {}, onProgress = () => {}) => {
|
|
325
|
+
if (file.size > 25000000) return {error: 'invalid_field_size', body: {code: 'invalid_field_size'}}
|
|
326
|
+
const identity = JSON.stringify([this.accid, this.uid, this.ref, this.mask, this.profile_id, this.tokensession])
|
|
327
|
+
if (state.identity && state.identity !== identity) throw new Error('Upload identity changed')
|
|
328
|
+
state.identity = identity
|
|
329
|
+
const checkIdentity = () => {
|
|
330
|
+
if (identity !== JSON.stringify([this.accid, this.uid, this.ref, this.mask, this.profile_id, this.tokensession])) throw new Error('Upload identity changed')
|
|
331
|
+
}
|
|
332
|
+
if (state.file) return state.file
|
|
333
|
+
// A completed PUT can still be finalized after the PUT URL expires.
|
|
334
|
+
if (state.upload && !state.putDone && state.upload.upload_expires_at <= sb.Date.now()) {
|
|
335
|
+
delete state.upload
|
|
336
|
+
delete state.key
|
|
337
|
+
}
|
|
338
|
+
if (!state.key) state.key = sb.randomString(36)
|
|
339
|
+
if (!state.metadata) state.metadata = {
|
|
340
|
+
category: 'attachment', name: file.name,
|
|
341
|
+
content_type: file.type || 'application/octet-stream', size: file.size, ttl_seconds: 0,
|
|
342
|
+
}
|
|
343
|
+
if (!state.upload) {
|
|
344
|
+
const created = await this.attachmentJSON('/uploads', {...state.metadata, idempotency_key: state.key})
|
|
345
|
+
checkIdentity()
|
|
346
|
+
if (!created.upload?.upload_id || !created.upload.upload_url) throw new Error('Missing upload session')
|
|
347
|
+
state.upload = created.upload
|
|
348
|
+
}
|
|
349
|
+
if (!state.putDone) {
|
|
350
|
+
checkIdentity()
|
|
351
|
+
const report = (percent) => {
|
|
352
|
+
if (state.progress === percent) return
|
|
353
|
+
state.progress = percent
|
|
354
|
+
onProgress(percent)
|
|
355
|
+
}
|
|
356
|
+
report(0)
|
|
357
|
+
// XHR exposes upload progress; this request still bypasses the API client.
|
|
358
|
+
await new Promise((resolve, reject) => {
|
|
359
|
+
const xhr = new sb.XMLHttpRequest()
|
|
360
|
+
xhr.upload.onprogress = (event) => {
|
|
361
|
+
if (event.lengthComputable) report(Math.min(100, Math.floor(event.loaded / event.total * 100)))
|
|
387
362
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
363
|
+
xhr.open('PUT', state.upload.upload_url, true)
|
|
364
|
+
xhr.withCredentials = false
|
|
365
|
+
Object.entries(state.upload.required_headers || {}).forEach(([key, value]) => xhr.setRequestHeader(key, value))
|
|
366
|
+
xhr.onload = () => xhr.status >= 200 && xhr.status < 300 ? resolve() : reject(new Error(`Upload failed: HTTP ${xhr.status}`))
|
|
367
|
+
xhr.onerror = () => reject(new Error('Network error during upload'))
|
|
368
|
+
xhr.onabort = () => reject(new Error('Upload aborted'))
|
|
369
|
+
xhr.ontimeout = () => reject(new Error('Upload timed out'))
|
|
370
|
+
xhr.send(file)
|
|
393
371
|
})
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
372
|
+
report(100)
|
|
373
|
+
state.putDone = true
|
|
374
|
+
}
|
|
375
|
+
checkIdentity()
|
|
376
|
+
const finished = await this.attachmentJSON(`/uploads/${encodeURIComponent(state.upload.upload_id)}/finish`)
|
|
377
|
+
checkIdentity()
|
|
378
|
+
const f = finished.file
|
|
379
|
+
if (!f?.id || !f.url || f.error_code) throw new Error('File is not available')
|
|
380
|
+
state.file = {url: f.url, v: 'cdn', file_id: f.id}
|
|
381
|
+
return state.file
|
|
397
382
|
}
|
|
398
383
|
|
|
399
384
|
time = (_) => ajax.setBaseUrl(this.ApiURL).withCredentials(true).setParser('json').get('/time')
|
package/src/store/api_fake.js
CHANGED
|
@@ -66,7 +66,7 @@ export default class {
|
|
|
66
66
|
sendLostFocus = (data) => OK200
|
|
67
67
|
sendContentView = (data) => OK200
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
uploadAttachment = async (file) => OK200
|
|
70
70
|
|
|
71
71
|
time = (_) => Promise.resolve({code: 200, body: new sb.Date().toISOString()})
|
|
72
72
|
updateUser = (data) => OK200
|
package/src/store/attachments.js
CHANGED
|
@@ -4,7 +4,7 @@ export async function uploadAttachments(attachments, upload) {
|
|
|
4
4
|
let error, errorMessage
|
|
5
5
|
for (const attachment of attachments) {
|
|
6
6
|
try {
|
|
7
|
-
const result = await upload(attachment._file)
|
|
7
|
+
const result = await upload(attachment._file, attachment._upload || (attachment._upload = {}))
|
|
8
8
|
if (result.body && result.body.code === 'invalid_field_size') {
|
|
9
9
|
errorMessage = 'cannot_send_too_big_file'
|
|
10
10
|
}
|
|
@@ -13,6 +13,10 @@ export async function uploadAttachments(attachments, upload) {
|
|
|
13
13
|
continue
|
|
14
14
|
}
|
|
15
15
|
attachment.url = result.url
|
|
16
|
+
if (result.file_id) {
|
|
17
|
+
attachment.v = 'cdn'
|
|
18
|
+
attachment.file_id = result.file_id
|
|
19
|
+
}
|
|
16
20
|
} catch (err) {
|
|
17
21
|
error = error || err
|
|
18
22
|
}
|
package/src/store/store.js
CHANGED
|
@@ -1450,7 +1450,7 @@ export default class Store {
|
|
|
1450
1450
|
if (att.type !== 'file') return
|
|
1451
1451
|
if (!att._file) return
|
|
1452
1452
|
files.push(att)
|
|
1453
|
-
if (ImageTypes.includes(att._file.type)) {
|
|
1453
|
+
if (!att._upload?.file && ImageTypes.includes(att._file.type)) {
|
|
1454
1454
|
let {width, height, url} = await loadImage(att._file)
|
|
1455
1455
|
att.url = url
|
|
1456
1456
|
att.width = width
|
|
@@ -1463,14 +1463,21 @@ export default class Store {
|
|
|
1463
1463
|
await this.convoGate.entry()
|
|
1464
1464
|
// upload file first
|
|
1465
1465
|
|
|
1466
|
-
const uploaded = await uploadAttachments(files, (file) => this.api.
|
|
1466
|
+
const uploaded = await uploadAttachments(files, (file, state) => this.api.uploadAttachment(file, state, () => this.pubsub.publish('events', event)))
|
|
1467
1467
|
event._error = uploaded.error
|
|
1468
1468
|
if (uploaded.errorMessage) event._error_message = uploaded.errorMessage
|
|
1469
1469
|
|
|
1470
1470
|
if (event._error) return this.onWs(event) // dont send error msg
|
|
1471
1471
|
if (cid && cid.startsWith('cslight')) return
|
|
1472
1472
|
|
|
1473
|
-
|
|
1473
|
+
// Keep browser Files and upload sessions locally for retry, never in the event JSON.
|
|
1474
|
+
const outgoing = {...event, data: {...event.data}}
|
|
1475
|
+
if (event.data.message) outgoing.data.message = {...event.data.message, attachments: event.data.message.attachments?.map(att => {
|
|
1476
|
+
const {_file, _upload, ...attachment} = att
|
|
1477
|
+
return attachment
|
|
1478
|
+
})}
|
|
1479
|
+
let {body: ret, error} = await this.api.sendConvoEvent(cid, outgoing)
|
|
1480
|
+
error = error || ret?.error
|
|
1474
1481
|
if (error) {
|
|
1475
1482
|
event._error = error
|
|
1476
1483
|
this.onWs(event)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|