@live-change/image-frontend 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/front/index.html +11 -0
- package/front/public/favicon.ico +0 -0
- package/front/public/images/empty-photo.svg +38 -0
- package/front/public/images/empty-user-photo.svg +33 -0
- package/front/public/images/logo.svg +34 -0
- package/front/public/images/logo128.png +0 -0
- package/front/src/App.vue +32 -0
- package/front/src/EditorTest.vue +28 -0
- package/front/src/Image.vue +137 -0
- package/front/src/ImageCrop.vue +382 -0
- package/front/src/ImageEditor.vue +211 -0
- package/front/src/ImageUpload.js +132 -0
- package/front/src/NavBar.vue +105 -0
- package/front/src/UploadTest.vue +57 -0
- package/front/src/dom.js +82 -0
- package/front/src/entry-client.js +6 -0
- package/front/src/entry-server.js +6 -0
- package/front/src/imageResizer.js +5 -0
- package/front/src/imageUploads.js +29 -0
- package/front/src/imageUtils.js +438 -0
- package/front/src/preprocessImageFile.js +122 -0
- package/front/src/router.js +41 -0
- package/front/vite.config.js +18 -0
- package/index.js +7 -0
- package/package.json +76 -0
- package/server/init.js +5 -0
- package/server/services.config.js +17 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { ref, onUnmounted, watch } from 'vue'
|
|
2
|
+
import { api as useApi } from '@live-change/vue3-ssr'
|
|
3
|
+
import { Upload } from '@live-change/upload-frontend'
|
|
4
|
+
import { blobToDataUrl } from "./imageUtils.js"
|
|
5
|
+
import preProcessImageFile from "./preprocessImageFile.js";
|
|
6
|
+
|
|
7
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
8
|
+
|
|
9
|
+
class ImageUpload {
|
|
10
|
+
constructor(purpose, uploadable, options = {}) {
|
|
11
|
+
const blob = uploadable.file
|
|
12
|
+
const {
|
|
13
|
+
fileName = blob?.name,
|
|
14
|
+
api = useApi(options.appContext),
|
|
15
|
+
uploadOptions = {}
|
|
16
|
+
} = options
|
|
17
|
+
this.options = options
|
|
18
|
+
this.purpose = purpose
|
|
19
|
+
this.fileName = fileName
|
|
20
|
+
this.uploadOptions = { appContext: options.appContext, api, ...uploadOptions }
|
|
21
|
+
this.api = api
|
|
22
|
+
this.id = options.generateId ? api.uid() : options.id
|
|
23
|
+
this.localUid = api.uid()
|
|
24
|
+
this.blob = null
|
|
25
|
+
this.uploadable = uploadable
|
|
26
|
+
this.size = null
|
|
27
|
+
const clientConfig = api.getServiceDefinition('image')?.clientConfig
|
|
28
|
+
this.config = clientConfig.upload
|
|
29
|
+
|
|
30
|
+
this.ownerType = options.ownerType
|
|
31
|
+
this.owner = options.owner
|
|
32
|
+
if(!this.ownerType || !this.owner) {
|
|
33
|
+
const client = api.client.value
|
|
34
|
+
if(client.user) {
|
|
35
|
+
this.ownerType = 'user_User'
|
|
36
|
+
this.owner = client.user
|
|
37
|
+
} else {
|
|
38
|
+
this.ownerType = 'session_Session'
|
|
39
|
+
this.owner = client.session
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.url = ref()
|
|
44
|
+
this.canvas = ref()
|
|
45
|
+
|
|
46
|
+
this.error = ref()
|
|
47
|
+
this.state = ref('starting')
|
|
48
|
+
|
|
49
|
+
this.prepared = false
|
|
50
|
+
|
|
51
|
+
this.preparePromise = null
|
|
52
|
+
this.uploadPromise = null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
prepare() {
|
|
56
|
+
if(!this.preparePromise) this.preparePromise = (async () => {
|
|
57
|
+
if(this.options.unpreparedPreview) {
|
|
58
|
+
if(this.uploadable.canvas) {
|
|
59
|
+
if(this.options.saveCanvas) this.canvas.value = this.uploadable.canvas
|
|
60
|
+
this.url.value = this.uploadable.canvas.toDataURL(this.options.fileType || 'image/png')
|
|
61
|
+
} else if(this.uploadable.file) {
|
|
62
|
+
this.url.value = await blobToDataUrl(this.uploadable.file)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const processed = await preProcessImageFile(this.uploadable, { ...this.config, ...this.options })
|
|
67
|
+
this.blob = processed.blob
|
|
68
|
+
this.size = processed.size
|
|
69
|
+
|
|
70
|
+
if(this.options.preparedPreview) {
|
|
71
|
+
if(processed.canvas) {
|
|
72
|
+
if(this.options.saveCanvas) this.canvas.value = processed.canvas
|
|
73
|
+
this.url.value = processed.canvas.toDataURL(this.options.fileType || 'image/png')
|
|
74
|
+
} else {
|
|
75
|
+
this.url.value = await blobToDataUrl(this.blob)
|
|
76
|
+
}
|
|
77
|
+
//console.log("PREPARED PREVIEW!", processed, '=>', this.url.value)
|
|
78
|
+
}
|
|
79
|
+
const extension = this.blob.type.split('/')[1]
|
|
80
|
+
if(!this.fileName) {
|
|
81
|
+
this.fileName = 'unknown.' + extension
|
|
82
|
+
} else {
|
|
83
|
+
const nameSplit = this.fileName.split('.')
|
|
84
|
+
const currentExtension = nameSplit[nameSplit.length - 1]
|
|
85
|
+
if(currentExtension != extension) {
|
|
86
|
+
this.fileName = nameSplit.slice(0, -1).concat([extension]).join('.')
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.prepared = true
|
|
90
|
+
})()
|
|
91
|
+
return this.preparePromise
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async upload() {
|
|
95
|
+
if(!this.uploadPromise) this.uploadPromise = (async () => {
|
|
96
|
+
await this.prepare()
|
|
97
|
+
await sleep(2000)
|
|
98
|
+
|
|
99
|
+
this.state.value = 'uploading'
|
|
100
|
+
|
|
101
|
+
this.fileUpload = new Upload(this.purpose, this.blob, { fileName: this.fileName, ...this.uploadOptions })
|
|
102
|
+
await this.fileUpload.promise
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const imageParams = {
|
|
106
|
+
image: this.id,
|
|
107
|
+
name: this.uploadable.name || this.uploadable.file?.name || this.options.name || 'unknown',
|
|
108
|
+
width: this.size.width,
|
|
109
|
+
height: this.size.height,
|
|
110
|
+
upload: this.fileUpload.id,
|
|
111
|
+
purpose: this.purpose,
|
|
112
|
+
ownerType: this.ownerType,
|
|
113
|
+
owner: this.owner,
|
|
114
|
+
crop: this.options.crop
|
|
115
|
+
}
|
|
116
|
+
console.log("CREATINNG IMAGE!", imageParams)
|
|
117
|
+
this.id = await this.api.command(["image", "createImage"], imageParams)
|
|
118
|
+
//this.url.value = `/api/image/image/${this.id}` - useless reload...
|
|
119
|
+
this.state.value = 'done'
|
|
120
|
+
} catch(error) {
|
|
121
|
+
this.error.value = error
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.state.value = 'done'
|
|
125
|
+
|
|
126
|
+
/// TODO: set this.url.value to server-side address
|
|
127
|
+
})()
|
|
128
|
+
return this.uploadPromise
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export default ImageUpload
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="surface-overlay py-3 px-6 shadow-2 flex align-items-center justify-content-between
|
|
3
|
+
relative md:sticky top-0 z-5"
|
|
4
|
+
style="min-height: 80px" key="navbar">
|
|
5
|
+
<img src="/images/logo.svg" alt="Image" height="40" class="mr-0 lg:mr-6">
|
|
6
|
+
<a v-ripple class="cursor-pointer block lg:hidden text-700 p-ripple"
|
|
7
|
+
v-styleclass="{ selector: '@next', enterClass: 'hidden', leaveToClass: 'hidden', hideOnOutsideClick: true }">
|
|
8
|
+
<i class="pi pi-bars text-4xl"></i>
|
|
9
|
+
</a>
|
|
10
|
+
<div class="align-items-center flex-grow-1 justify-content-between hidden lg:flex absolute lg:static w-full surface-overlay left-0 top-100 z-1 shadow-2 lg:shadow-none">
|
|
11
|
+
<ul class="list-none p-0 m-0 flex lg:align-items-center select-none flex-column lg:flex-row">
|
|
12
|
+
<li>
|
|
13
|
+
<a v-ripple class="flex px-6 p-3 lg:px-3 lg:py-2 align-items-center text-600 hover:text-900 hover:surface-100 font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple">
|
|
14
|
+
<i class="pi pi-home mr-2"></i>
|
|
15
|
+
<span>Home</span>
|
|
16
|
+
</a>
|
|
17
|
+
</li>
|
|
18
|
+
<li class="lg:relative">
|
|
19
|
+
<a v-ripple class="flex px-6 p-3 lg:px-3 lg:py-2 align-items-center text-600 hover:text-900 hover:surface-100 font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple"
|
|
20
|
+
v-styleclass="{ selector: '@next', enterClass: 'hidden', enterActiveClass: 'scalein', leaveToClass: 'hidden', leaveActiveClass: 'fadeout', hideOnOutsideClick: true }">
|
|
21
|
+
<i class="pi pi-users mr-2"></i>
|
|
22
|
+
<span>Customers</span>
|
|
23
|
+
<i class="pi pi-angle-down ml-auto lg:ml-3"></i>
|
|
24
|
+
</a>
|
|
25
|
+
<ul class="list-none py-3 px-6 m-0 lg:px-0 lg:py-0 border-round shadow-0 lg:shadow-2 lg:border-1 border-50 lg:absolute surface-overlay hidden origin-top w-full lg:w-15rem cursor-pointer">
|
|
26
|
+
<li>
|
|
27
|
+
<a v-ripple class="flex p-3 align-items-center text-600 hover:text-900 hover:surface-100 transition-colors transition-duration-150 p-ripple">
|
|
28
|
+
<i class="pi pi-user-plus mr-2"></i>
|
|
29
|
+
<span class="font-medium">Add New</span>
|
|
30
|
+
</a>
|
|
31
|
+
</li>
|
|
32
|
+
<li class="relative">
|
|
33
|
+
<a v-ripple class="flex p-3 align-items-center text-600 hover:text-900 hover:surface-100 transition-colors transition-duration-150 p-ripple"
|
|
34
|
+
v-styleclass="{ selector: '@next', enterClass: 'hidden', enterActiveClass: 'scalein', leaveToClass: 'hidden', leaveActiveClass: 'fadeout', hideOnOutsideClick: true }">
|
|
35
|
+
<i class="pi pi-search mr-2"></i>
|
|
36
|
+
<span class="font-medium">Search</span>
|
|
37
|
+
<i class="pi pi-angle-down ml-auto lg:-rotate-90"></i>
|
|
38
|
+
</a>
|
|
39
|
+
<ul class="list-none py-3 pl-3 m-0 lg:px-0 lg:py-0 border-round shadow-0 lg:shadow-2 lg:border-1 border-50 lg:absolute surface-overlay hidden origin-top w-full lg:w-15rem cursor-pointer left-100 top-0">
|
|
40
|
+
<li>
|
|
41
|
+
<a v-ripple class="flex p-3 align-items-center text-600 hover:text-900 hover:surface-100 transition-colors transition-duration-150 p-ripple">
|
|
42
|
+
<i class="pi pi-shopping-cart mr-2"></i>
|
|
43
|
+
<span class="font-medium">Purchases</span>
|
|
44
|
+
</a>
|
|
45
|
+
</li>
|
|
46
|
+
<li class="relative">
|
|
47
|
+
<a v-ripple class="flex p-3 align-items-center text-600 hover:text-900 hover:surface-100 transition-colors transition-duration-150 p-ripple">
|
|
48
|
+
<i class="pi pi-comments mr-2"></i>
|
|
49
|
+
<span class="font-medium">Messages</span>
|
|
50
|
+
</a>
|
|
51
|
+
</li>
|
|
52
|
+
</ul>
|
|
53
|
+
</li>
|
|
54
|
+
</ul>
|
|
55
|
+
</li>
|
|
56
|
+
<li>
|
|
57
|
+
<a v-ripple class="flex px-6 p-3 lg:px-3 lg:py-2 align-items-center text-600 hover:text-900 hover:surface-100 font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple">
|
|
58
|
+
<i class="pi pi-calendar mr-2"></i>
|
|
59
|
+
<span>Calendar</span>
|
|
60
|
+
</a>
|
|
61
|
+
</li>
|
|
62
|
+
<li>
|
|
63
|
+
<a v-ripple class="flex px-6 p-3 lg:px-3 lg:py-2 align-items-center text-600 hover:text-900 hover:surface-100 font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple">
|
|
64
|
+
<i class="pi pi-chart-line mr-2"></i>
|
|
65
|
+
<span>Stats</span>
|
|
66
|
+
</a>
|
|
67
|
+
</li>
|
|
68
|
+
</ul>
|
|
69
|
+
<ul class="list-none p-0 m-0 flex lg:align-items-center select-none flex-column lg:flex-row border-top-1 surface-border lg:border-top-none">
|
|
70
|
+
<li>
|
|
71
|
+
<a v-ripple class="flex px-6 p-3 lg:px-3 lg:py-2 align-items-center text-600 hover:text-900 hover:surface-100 font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple">
|
|
72
|
+
<i class="pi pi-inbox text-base lg:text-2xl mr-2 lg:mr-0"></i>
|
|
73
|
+
<span class="block lg:hidden font-medium">Inbox</span>
|
|
74
|
+
</a>
|
|
75
|
+
</li>
|
|
76
|
+
<li>
|
|
77
|
+
<a v-ripple class="flex px-6 p-3 lg:px-3 lg:py-3 align-items-center text-600 hover:text-900 hover:surface-100
|
|
78
|
+
font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple">
|
|
79
|
+
<i class="pi pi-bell text-base lg:text-2xl mr-2 lg:mr-0 p-overlay-badge">
|
|
80
|
+
<Badge value="2"></Badge>
|
|
81
|
+
</i>
|
|
82
|
+
<span class="block lg:hidden font-medium">Notifications</span>
|
|
83
|
+
</a>
|
|
84
|
+
</li>
|
|
85
|
+
<li class="border-top-1 surface-border lg:border-top-none">
|
|
86
|
+
<a v-ripple class="flex px-6 p-3 lg:px-3 lg:py-2 align-items-center hover:surface-100 font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple">
|
|
87
|
+
<img src="/images/empty-user-photo.svg" class="mr-3 lg:mr-0 border-circle" style="width: 28px; height: 28px"/>
|
|
88
|
+
<div class="block lg:hidden">
|
|
89
|
+
<div class="text-900 font-medium">Josephine Lillard</div>
|
|
90
|
+
<span class="text-600 font-medium text-sm">Marketing Specialist</span>
|
|
91
|
+
</div>
|
|
92
|
+
</a>
|
|
93
|
+
</li>
|
|
94
|
+
</ul>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<script setup>
|
|
100
|
+
import Badge from "primevue/badge"
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<style scoped>
|
|
104
|
+
|
|
105
|
+
</style>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="w-full sm:w-9 md:w-8 lg:w-6 surface-card p-4 shadow-2 border-round">
|
|
3
|
+
<div class="text-center mb-5">
|
|
4
|
+
<div class="text-900 text-3xl font-medium mb-3">
|
|
5
|
+
Upload Test
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div></div>
|
|
10
|
+
<!-- <pre>{{ JSON.stringify(upload, null, " ") }}</pre>-->
|
|
11
|
+
|
|
12
|
+
<FileInput @input="handleUpload" class="inline-block">
|
|
13
|
+
<Button label="Upload File" icon="pi pi-upload"></Button>
|
|
14
|
+
</FileInput>
|
|
15
|
+
|
|
16
|
+
<div v-if="image">
|
|
17
|
+
<h2>Image: {{ image }}</h2>
|
|
18
|
+
<Image :image="image" width="200" height="200" />
|
|
19
|
+
</div>
|
|
20
|
+
<!-- <UploadView v-if="upload" :upload="upload" cancelable class="surface-card shadow-2 border-round p-4 mt-3" />-->
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup>
|
|
25
|
+
|
|
26
|
+
import Button from "primevue/button"
|
|
27
|
+
import { ref, shallowRef, getCurrentInstance } from 'vue'
|
|
28
|
+
import { Upload, UploadView, FileInput } from "@live-change/upload-frontend"
|
|
29
|
+
import { uploadImage } from "./imageUploads.js"
|
|
30
|
+
import Image from "./Image.vue"
|
|
31
|
+
|
|
32
|
+
const upload = shallowRef()
|
|
33
|
+
const appContext = getCurrentInstance().appContext
|
|
34
|
+
|
|
35
|
+
const image = ref()
|
|
36
|
+
|
|
37
|
+
async function handleUpload(file) {
|
|
38
|
+
console.log("FILE", file)
|
|
39
|
+
upload.value = uploadImage('test', { file },
|
|
40
|
+
{ preparedPreview: true, appContext, generateId : true, noStart: true })
|
|
41
|
+
image.value = upload.value.id
|
|
42
|
+
console.log("START PREPARE!")
|
|
43
|
+
await upload.value.prepare()
|
|
44
|
+
console.log("START UPLOAD!")
|
|
45
|
+
await upload.value.upload()
|
|
46
|
+
image.value = upload.value.id
|
|
47
|
+
|
|
48
|
+
console.log("UPLOADED", upload.value)
|
|
49
|
+
|
|
50
|
+
//upload.value = null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<style scoped>
|
|
56
|
+
|
|
57
|
+
</style>
|
package/front/src/dom.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
function getElementPositionInWindow(element) {
|
|
2
|
+
let o = element
|
|
3
|
+
let x = 0
|
|
4
|
+
let y = 0
|
|
5
|
+
while (true) {
|
|
6
|
+
//console.log("OE", o, x, y)
|
|
7
|
+
x += o.offsetLeft
|
|
8
|
+
y += o.offsetTop
|
|
9
|
+
if(window.getComputedStyle(o).position == 'fixed') {
|
|
10
|
+
return { x, y }
|
|
11
|
+
}
|
|
12
|
+
o = o.offsetParent
|
|
13
|
+
if(!o) {
|
|
14
|
+
return {
|
|
15
|
+
x: x - document.documentElement.scrollLeft || 0,
|
|
16
|
+
y: y - document.documentElement.scrollTop || 0
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if(o.scrollLeft) x -= o.scrollLeft
|
|
20
|
+
if(o.scrollTop) y -= o.scrollTop
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getElementPositionInDocument(element) {
|
|
25
|
+
let o = element
|
|
26
|
+
let x = 0
|
|
27
|
+
let y = 0
|
|
28
|
+
while (true) {
|
|
29
|
+
x += o.offsetLeft
|
|
30
|
+
y += o.offsetTop
|
|
31
|
+
if(window.getComputedStyle(o).position == 'fixed') {
|
|
32
|
+
return { x, y }
|
|
33
|
+
}
|
|
34
|
+
o = o.offsetParent
|
|
35
|
+
if(!o) {
|
|
36
|
+
return {
|
|
37
|
+
x: x || 0,
|
|
38
|
+
y: y || 0
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if(o.scrollLeft) x -= o.scrollLeft
|
|
42
|
+
if(o.scrollTop) y -= o.scrollTop
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getElementPositionInElement(element, parent) {
|
|
47
|
+
let o = element
|
|
48
|
+
let x = 0
|
|
49
|
+
let y = 0
|
|
50
|
+
while (true) {
|
|
51
|
+
x += o.offsetLeft
|
|
52
|
+
y += o.offsetTop
|
|
53
|
+
if(window.getComputedStyle(o).position == 'fixed') {
|
|
54
|
+
return { x, y }
|
|
55
|
+
}
|
|
56
|
+
o = o.offsetParent
|
|
57
|
+
if(!o || o == parent) {
|
|
58
|
+
return {
|
|
59
|
+
x: x || 0,
|
|
60
|
+
y: y || 0
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if(o.scrollLeft) x -= o.scrollLeft
|
|
64
|
+
if(o.scrollTop) y -= o.scrollTop
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getScrollParent(element, includeHidden) {
|
|
69
|
+
let style = getComputedStyle(element)
|
|
70
|
+
const excludeStaticParent = style.position === "absolute"
|
|
71
|
+
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/
|
|
72
|
+
if (style.position === "fixed") return document.body
|
|
73
|
+
for (let parent = element; (parent = parent.parentElement);) {
|
|
74
|
+
style = getComputedStyle(parent)
|
|
75
|
+
if (excludeStaticParent && style.position === "static") continue
|
|
76
|
+
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent
|
|
77
|
+
}
|
|
78
|
+
return document.body
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
export { getElementPositionInWindow, getElementPositionInDocument, getElementPositionInElement, getScrollParent }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import ImageUpload from "./ImageUpload.js";
|
|
3
|
+
|
|
4
|
+
export const imageUploads = ref([])
|
|
5
|
+
|
|
6
|
+
async function handleUpload(upload, options) {
|
|
7
|
+
await upload.upload()
|
|
8
|
+
const existingIndex = imageUploads.value.findIndex(u => u.localUid === upload.localUid)
|
|
9
|
+
if(existingIndex !== -1) {
|
|
10
|
+
imageUploads.value.splice(existingIndex, 1)
|
|
11
|
+
}
|
|
12
|
+
console.log("IMAGE UPLOADS", imageUploads)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function uploadImage(purpose, uploadable, options = {}) {
|
|
16
|
+
const upload = new ImageUpload(purpose, uploadable, options)
|
|
17
|
+
if(options.id) {
|
|
18
|
+
const existingIndex = imageUploads.value.findIndex(u => u.id == options.id)
|
|
19
|
+
if( existingIndex != -1 ) {
|
|
20
|
+
imageUploads.value[existingIndex].cancel()
|
|
21
|
+
imageUploads.value[existingIndex] = upload
|
|
22
|
+
handleUpload(upload, options)
|
|
23
|
+
return upload
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
imageUploads.value.push(upload)
|
|
27
|
+
handleUpload(upload, options)
|
|
28
|
+
return upload
|
|
29
|
+
}
|