Skip to content

Commit f4f5979

Browse files
Dropbox: Fix Dropbox uploader crash caused by reactive Uppy instance - refs #7345
1 parent 8600e78 commit f4f5979

3 files changed

Lines changed: 125 additions & 54 deletions

File tree

assets/vue/components/dropbox/UppyModalUploader.vue

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
hideUploadButton: true,
99
showProgressDetails: false,
1010
proudlyDisplayPoweredByUppy: false,
11-
maxNumberOfFiles: 1
11+
maxNumberOfFiles: 1,
1212
}"
1313
@close="handleClose"
1414
:done-button-handler="handleDone"
1515
/>
1616
</template>
1717

1818
<script setup>
19-
import { ref, onMounted, onBeforeUnmount } from "vue"
19+
import { shallowRef, ref, onMounted, onBeforeUnmount, markRaw } from "vue"
2020
import Uppy from "@uppy/core"
2121
import { DashboardModal } from "@uppy/vue"
2222
import "@uppy/core/dist/style.css"
@@ -26,38 +26,56 @@ const props = defineProps({
2626
visible: { type: Boolean, default: false },
2727
})
2828
const emit = defineEmits(["close", "files-selected", "file-added"])
29-
30-
const uppy = ref(null)
29+
const uppy = shallowRef(null)
3130
const ready = ref(false)
31+
let onFileAddedHandler = null
3232
3333
function handleClose() {
3434
emit("close")
3535
}
3636
3737
function handleDone() {
3838
// Return all selected files without uploading
39-
const files = Object.values(uppy.value.getFiles()).map(f => f.data)
39+
const files = (uppy.value?.getFiles?.() ?? []).map((f) => f.data)
4040
if (files.length) emit("files-selected", files)
4141
handleClose()
4242
}
4343
4444
onMounted(() => {
45-
uppy.value = new Uppy({
46-
autoProceed: false,
47-
allowMultipleUploads: true,
48-
restrictions: { maxNumberOfFiles: null },
49-
})
45+
uppy.value = markRaw(
46+
new Uppy({
47+
autoProceed: false,
48+
allowMultipleUploads: true,
49+
restrictions: { maxNumberOfFiles: null },
50+
}),
51+
)
5052
51-
uppy.value.on("file-added", file => emit("file-added", file))
53+
onFileAddedHandler = (file) => emit("file-added", file)
54+
uppy.value?.on?.("file-added", onFileAddedHandler)
5255
5356
ready.value = true
5457
})
5558
5659
onBeforeUnmount(() => {
57-
if (uppy.value) {
58-
try { uppy.value.cancelAll() } catch {}
59-
try { uppy.value.reset() } catch {}
60-
uppy.value = null
61-
}
60+
if (!uppy.value) return
61+
62+
try {
63+
if (onFileAddedHandler) {
64+
uppy.value?.off?.("file-added", onFileAddedHandler)
65+
}
66+
} catch {}
67+
68+
try {
69+
uppy.value.cancelAll()
70+
} catch {}
71+
try {
72+
uppy.value.reset()
73+
} catch {}
74+
75+
try {
76+
uppy.value.close?.()
77+
} catch {}
78+
79+
uppy.value = null
6280
})
6381
</script>

assets/vue/views/dropbox/DropboxCreate.vue

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@
1010
</template>
1111
<template #end>
1212
<RouterLink :to="{ name: 'DropboxListSent', params: $route.params, query: $route.query }">
13-
<BaseButton type="black" icon="arrow-left" :label="t('Back')" />
13+
<BaseButton
14+
type="black"
15+
icon="arrow-left"
16+
:label="t('Back')"
17+
/>
1418
</RouterLink>
1519
</template>
1620
</BaseToolbar>
1721

18-
<div class="grid mt-5 gap-6" style="grid-template-columns: 1.6fr 1fr">
22+
<div
23+
class="grid mt-5 gap-6"
24+
style="grid-template-columns: 1.6fr 1fr"
25+
>
1926
<!-- LEFT: files -->
2027
<div>
2128
<div class="rounded-lg border border-gray-50 bg-white shadow-sm">
@@ -37,7 +44,10 @@
3744
/>
3845
</div>
3946

40-
<div v-if="pickedFiles.length" class="mt-3">
47+
<div
48+
v-if="pickedFiles.length"
49+
class="mt-3"
50+
>
4151
<div class="text-sm text-gray-600 mb-1">{{ t("Selected files") }}</div>
4252
<ul class="text-sm space-y-1">
4353
<li
@@ -60,7 +70,10 @@
6070
</ul>
6171
</div>
6272

63-
<div v-else class="mt-3 text-sm text-gray-500">
73+
<div
74+
v-else
75+
class="mt-3 text-sm text-gray-500"
76+
>
6477
{{ t("No file selected.") }}
6578
</div>
6679
</div>
@@ -78,7 +91,11 @@
7891
/>
7992

8093
<label class="inline-flex items-center gap-3 mt-3">
81-
<input id="overwrite" type="checkbox" v-model="overwrite" />
94+
<input
95+
id="overwrite"
96+
type="checkbox"
97+
v-model="overwrite"
98+
/>
8299
<span class="text-sm">{{ t("Overwrite previous versions of same document?") }}</span>
83100
</label>
84101
</div>
@@ -90,9 +107,7 @@
90107
<div class="rounded-lg border border-gray-50 bg-white shadow-sm">
91108
<div class="px-4 py-3 border-b text-sm font-medium">{{ t("Sharing") }}</div>
92109
<div class="p-4">
93-
<label class="block text-sm mb-1">
94-
{{ t("Recipients") }} <span class="text-red-600">*</span>
95-
</label>
110+
<label class="block text-sm mb-1"> {{ t("Recipients") }} <span class="text-red-600">*</span> </label>
96111
<BaseSelect
97112
v-model="recipients"
98113
:options="recipientOptions"
@@ -105,15 +120,22 @@
105120
:class="{ 'ring-1 ring-red-500 rounded-md': submitted && !hasSelectedRecipient }"
106121
/>
107122
<small class="text-gray-500 block mt-1">
108-
{{ t('Tip: choose “— Just upload —” to store without sending to anyone.') }}
123+
{{ t("Tip: choose “— Just upload —” to store without sending to anyone.") }}
109124
</small>
110-
<div v-if="submitted && !hasSelectedRecipient" class="text-sm text-red-600 mt-1">
111-
{{ t('Please select at least one recipient (“— Just upload —” or any user)') }}
125+
<div
126+
v-if="submitted && !hasSelectedRecipient"
127+
class="text-sm text-red-600 mt-1"
128+
>
129+
{{ t("Please select at least one recipient (“— Just upload —” or any user)") }}
112130
</div>
113131

114132
<div class="flex justify-end gap-2 mt-6">
115133
<RouterLink :to="{ name: 'DropboxListSent', params: $route.params, query: $route.query }">
116-
<BaseButton type="black" icon="xmark" :label="t('Cancel')" />
134+
<BaseButton
135+
type="black"
136+
icon="xmark"
137+
:label="t('Cancel')"
138+
/>
117139
</RouterLink>
118140
<BaseButton
119141
type="primary"
@@ -126,7 +148,10 @@
126148
</div>
127149
</div>
128150

129-
<div v-if="isUploading" class="text-xs text-gray-500 mt-2 text-right">
151+
<div
152+
v-if="isUploading"
153+
class="text-xs text-gray-500 mt-2 text-right"
154+
>
130155
{{ t("Uploading... please keep this tab open.") }}
131156
</div>
132157
</div>
@@ -135,7 +160,7 @@
135160
</template>
136161

137162
<script setup>
138-
import { computed, onBeforeUnmount, onMounted, ref } from "vue"
163+
import { computed, markRaw, onBeforeUnmount, onMounted, ref, shallowRef } from "vue"
139164
import { useRoute, useRouter } from "vue-router"
140165
import { useI18n } from "vue-i18n"
141166
import Uppy from "@uppy/core"
@@ -153,8 +178,8 @@ import service from "../../services/dropbox"
153178
const { t } = useI18n()
154179
const route = useRoute()
155180
const router = useRouter()
181+
const uppy = shallowRef(null)
156182
157-
const uppy = ref(null)
158183
const pickedFiles = ref([])
159184
const description = ref("")
160185
const overwrite = ref(false)
@@ -164,41 +189,61 @@ const isUploading = ref(false)
164189
// recipients
165190
const recipients = ref([])
166191
const recipientOptions = ref([])
192+
let syncPicked = null
167193
168194
onMounted(() => {
169-
uppy.value = new Uppy({
170-
autoProceed: false,
171-
allowMultipleUploads: true,
172-
restrictions: { maxNumberOfFiles: null },
173-
})
195+
uppy.value = markRaw(
196+
new Uppy({
197+
autoProceed: false,
198+
allowMultipleUploads: true,
199+
restrictions: { maxNumberOfFiles: null },
200+
}),
201+
)
174202
175-
// sync picked files when user adds/removes in dashboard
176-
const syncPicked = () => {
177-
pickedFiles.value = Object.values(uppy.value?.getFiles?.() ?? {}).map((f) => f.data)
203+
syncPicked = () => {
204+
const files = uppy.value?.getFiles?.() ?? []
205+
pickedFiles.value = files.map((f) => f.data)
178206
}
179207
uppy.value?.on?.("file-added", syncPicked)
180208
uppy.value?.on?.("file-removed", syncPicked)
181209
})
182210
183211
onBeforeUnmount(() => {
184-
if (uppy.value) {
185-
try { uppy.value.cancelAll() } catch {}
186-
try { uppy.value.reset() } catch {}
187-
uppy.value = null
188-
}
212+
if (!uppy.value) return
213+
214+
try {
215+
if (syncPicked) {
216+
uppy.value?.off?.("file-added", syncPicked)
217+
uppy.value?.off?.("file-removed", syncPicked)
218+
}
219+
} catch {}
220+
221+
try {
222+
uppy.value.cancelAll()
223+
} catch {}
224+
try {
225+
uppy.value.reset()
226+
} catch {}
227+
228+
try {
229+
uppy.value.close?.()
230+
} catch {}
231+
232+
uppy.value = null
189233
})
190234
191235
function removePicked(file) {
192236
// remove by id match in uppy store
193-
const record = (uppy.value?.getFiles?.() || []).find((r) => r.data === file)
237+
const list = uppy.value?.getFiles?.() ?? []
238+
const record = list.find((r) => r.data === file)
194239
if (record) uppy.value?.removeFile?.(record.id)
195240
else pickedFiles.value = pickedFiles.value.filter((f) => f !== file)
196241
}
197242
198243
// utils
199244
function humanSize(bytes) {
200-
const units = ["B","KB","MB","GB","TB"]
201-
const i = bytes > 0 ? Math.floor(Math.log(bytes)/Math.log(1024)) : 0
245+
const units = ["B", "KB", "MB", "GB", "TB"]
246+
const i = bytes > 0 ? Math.floor(Math.log(bytes) / Math.log(1024)) : 0
202247
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`
203248
}
204249
@@ -253,7 +298,9 @@ async function submit() {
253298
description.value = ""
254299
overwrite.value = false
255300
recipients.value = []
256-
try { uppy.value?.reset() } catch {}
301+
try {
302+
uppy.value?.reset?.()
303+
} catch {}
257304
pickedFiles.value = []
258305
259306
// Navigate back to list (sent)

assets/vue/views/dropbox/DropboxUpdateDialog.vue

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,26 @@
88
<div class="space-y-4">
99
<!-- Summary -->
1010
<div class="text-sm text-gray-600">
11-
<span class="mr-2">{{ t('Current') }}:</span>
11+
<span class="mr-2">{{ t("Current") }}:</span>
1212
<strong>{{ fileTitle }}</strong>
1313
</div>
1414

1515
<!-- Uploader -->
1616
<div class="border rounded p-3">
1717
<div class="flex items-center justify-between">
18-
<div class="font-medium">{{ t('New version') }}</div>
18+
<div class="font-medium">{{ t("New version") }}</div>
1919
<BaseButton
2020
type="primary"
2121
icon="file-upload"
2222
:label="t('Choose file')"
2323
@click="showUploader = true"
2424
/>
2525
</div>
26-
<div v-if="pickedName" class="text-xs text-gray-500 mt-2">
27-
{{ t('{0} selected', [pickedName]) }}
26+
<div
27+
v-if="pickedName"
28+
class="text-xs text-gray-500 mt-2"
29+
>
30+
{{ t("{0} selected", [pickedName]) }}
2831
</div>
2932

3033
<!-- Rename toggle -->
@@ -35,7 +38,10 @@
3538
class="h-4 w-4"
3639
v-model="renameTitle"
3740
/>
38-
<label for="renameTitle" class="text-sm">
41+
<label
42+
for="renameTitle"
43+
class="text-sm"
44+
>
3945
{{ t("Change title to selected filename") }}
4046
</label>
4147
</div>
@@ -50,7 +56,7 @@
5056
<!-- Folder -->
5157
<div class="grid gap-3 md:grid-cols-2">
5258
<div>
53-
<label class="block text-sm mb-1">{{ t('Folder') }}</label>
59+
<label class="block text-sm mb-1">{{ t("Folder") }}</label>
5460
<BaseSelect
5561
v-model="categoryId"
5662
:options="folderOptions"

0 commit comments

Comments
 (0)