@sierra-95/svelte-scaffold 1.2.5 → 1.2.7
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/dist/Modules/FilePicker/Actions/delete.svelte +3 -3
- package/dist/Modules/FilePicker/Actions/download.svelte +75 -25
- package/dist/Modules/FilePicker/Upload/upload.svelte +42 -2
- package/dist/stores/modules/fileInput.d.ts +2 -0
- package/dist/stores/modules/fileInput.js +3 -1
- package/package.json +1 -1
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
|
|
22
22
|
async function deleteSubmissions() {
|
|
23
23
|
const store = get(fileInputStore);
|
|
24
|
-
const
|
|
24
|
+
const itemsToDelete = store.submissions;
|
|
25
25
|
|
|
26
|
-
if (!
|
|
26
|
+
if (!itemsToDelete || itemsToDelete.length === 0) return;
|
|
27
27
|
if (!$fileInputConfig.serverDeleteUrl) return;
|
|
28
28
|
|
|
29
29
|
try {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
headers: {
|
|
34
34
|
'Content-Type': 'application/json'
|
|
35
35
|
},
|
|
36
|
-
body: JSON.stringify(
|
|
36
|
+
body: JSON.stringify(itemsToDelete)
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
const data = await response.json();
|
|
@@ -1,61 +1,111 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { get } from "svelte/store";
|
|
3
3
|
import {MenuItem, fileInputStore, fileInputConfig, addToast} from "../../../index.js";
|
|
4
|
-
|
|
4
|
+
import type {FileInputStoreMediaItem} from '../../../index.js';
|
|
5
5
|
let {
|
|
6
6
|
openActionsMenu = $bindable(),
|
|
7
7
|
} = $props();
|
|
8
8
|
|
|
9
|
+
let AuthorizedMediaItems = $state([]);
|
|
10
|
+
|
|
11
|
+
async function download(item: FileInputStoreMediaItem) {
|
|
12
|
+
try {
|
|
13
|
+
const res = await fetch($fileInputConfig.serverDownloadUrl + `?r2_key=${item.r2_key}`, {
|
|
14
|
+
method: 'GET'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
addToast({
|
|
19
|
+
status: 'error',
|
|
20
|
+
message: await res.text() || 'Download failed'
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const blob = await res.blob();
|
|
26
|
+
const url = URL.createObjectURL(blob);
|
|
27
|
+
const a = document.createElement('a');
|
|
28
|
+
a.href = url;
|
|
29
|
+
a.download = item.original_name || 'download';
|
|
30
|
+
|
|
31
|
+
document.body.appendChild(a);
|
|
32
|
+
a.click();
|
|
33
|
+
a.remove();
|
|
34
|
+
|
|
35
|
+
URL.revokeObjectURL(url);
|
|
36
|
+
|
|
37
|
+
} catch (err) {
|
|
38
|
+
addToast({
|
|
39
|
+
status: 'error',
|
|
40
|
+
message: `Downloading ${item.original_name} failed: ${err}`
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
9
45
|
async function handleDownload() {
|
|
10
|
-
|
|
11
|
-
const store = get(fileInputStore);
|
|
12
|
-
if (!store.submissions?.length) {
|
|
46
|
+
if (!AuthorizedMediaItems.length) {
|
|
13
47
|
addToast({
|
|
14
48
|
status: 'error',
|
|
15
|
-
message: '
|
|
49
|
+
message: 'No files to download'
|
|
16
50
|
});
|
|
17
51
|
return;
|
|
18
|
-
}
|
|
52
|
+
}
|
|
19
53
|
try {
|
|
20
54
|
$fileInputStore.processing = true;
|
|
21
|
-
for (const item of
|
|
22
|
-
|
|
23
|
-
await download(item.id);
|
|
55
|
+
for (const item of AuthorizedMediaItems) {
|
|
56
|
+
await download(item);
|
|
24
57
|
}
|
|
25
58
|
} finally {
|
|
26
59
|
$fileInputStore.processing = false;
|
|
27
|
-
fileInputStore.update(store => {
|
|
28
|
-
store.submissions = [];
|
|
29
|
-
return store;
|
|
30
|
-
});
|
|
31
60
|
}
|
|
32
61
|
}
|
|
33
62
|
|
|
34
|
-
async function
|
|
63
|
+
async function fetchAuthorizedItems() {
|
|
64
|
+
openActionsMenu = false;
|
|
65
|
+
const store = get(fileInputStore);
|
|
66
|
+
if (!store.submissions?.length) {
|
|
67
|
+
addToast({
|
|
68
|
+
status: 'error',
|
|
69
|
+
message: 'An error occurred while processing your request.'
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
35
73
|
try {
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
74
|
+
$fileInputStore.processing = true;
|
|
75
|
+
const ids = store.submissions
|
|
76
|
+
.map(item => item.id)
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
|
|
79
|
+
const res = await fetch($fileInputConfig.serverDownloadUrl, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json'
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify(ids)
|
|
39
85
|
});
|
|
40
86
|
|
|
41
87
|
if (!res.ok) {
|
|
88
|
+
const message = await res.text();
|
|
42
89
|
addToast({
|
|
43
90
|
status: 'error',
|
|
44
|
-
message:
|
|
91
|
+
message: message || 'Download failed'
|
|
45
92
|
});
|
|
46
93
|
return;
|
|
47
94
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
a.href = url;
|
|
52
|
-
a.click();
|
|
53
|
-
a.remove();
|
|
95
|
+
AuthorizedMediaItems = await res.json();
|
|
96
|
+
await handleDownload();
|
|
97
|
+
|
|
54
98
|
} catch (err) {
|
|
55
99
|
addToast({
|
|
56
100
|
status: 'error',
|
|
57
101
|
message: `Download failed: ${err}`
|
|
58
102
|
});
|
|
103
|
+
} finally {
|
|
104
|
+
$fileInputStore.processing = false;
|
|
105
|
+
fileInputStore.update(store => {
|
|
106
|
+
store.submissions = [];
|
|
107
|
+
return store;
|
|
108
|
+
});
|
|
59
109
|
}
|
|
60
110
|
}
|
|
61
111
|
</script>
|
|
@@ -64,5 +114,5 @@
|
|
|
64
114
|
icon="fa fa-download"
|
|
65
115
|
iconGap="10px"
|
|
66
116
|
iconSize="15px"
|
|
67
|
-
onclick={
|
|
117
|
+
onclick={fetchAuthorizedItems}>Download
|
|
68
118
|
</MenuItem>
|
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
|
|
4
4
|
async function handleUpload() {
|
|
5
5
|
if( $fileInputStore.selectedFiles.length === 0 || !$fileInputConfig.serverUploadUrl) return;
|
|
6
|
-
|
|
6
|
+
if( !$fileInputConfig.user_id) {
|
|
7
|
+
addToast({
|
|
8
|
+
status: 'error',
|
|
9
|
+
message: 'User ID is required for upload',
|
|
10
|
+
persistent: true
|
|
11
|
+
});
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
7
14
|
try {
|
|
8
15
|
$fileInputStore.processing = true;
|
|
9
16
|
const formData = new FormData();
|
|
10
17
|
|
|
11
18
|
$fileInputStore.selectedFiles.forEach(file => formData.append('files', file));
|
|
19
|
+
formData.append('user_id', $fileInputConfig.user_id);
|
|
12
20
|
|
|
13
21
|
const res = await fetch($fileInputConfig.serverUploadUrl, {
|
|
14
22
|
method: 'POST',
|
|
@@ -35,6 +43,10 @@
|
|
|
35
43
|
status: 'success',
|
|
36
44
|
message: 'Files uploaded successfully!'
|
|
37
45
|
});
|
|
46
|
+
|
|
47
|
+
if($fileInputStore.onUpload){
|
|
48
|
+
$fileInputStore.onUpload();
|
|
49
|
+
}
|
|
38
50
|
} catch (err) {
|
|
39
51
|
addToast({
|
|
40
52
|
status: 'error',
|
|
@@ -45,11 +57,39 @@
|
|
|
45
57
|
$fileInputStore.processing = false;
|
|
46
58
|
}
|
|
47
59
|
}
|
|
60
|
+
|
|
61
|
+
async function handleStorageStatus(){
|
|
62
|
+
if (($fileInputConfig.usedBytes == null || $fileInputConfig.maxBytes == null)) {
|
|
63
|
+
addToast({
|
|
64
|
+
status: 'warning',
|
|
65
|
+
message: 'Unable to calculate storage usage. Please configure your cloud storage settings.',
|
|
66
|
+
persistent: true,
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const selectedSize = $fileInputStore.selectedFiles.reduce(
|
|
71
|
+
(acc, file) => acc + file.size,
|
|
72
|
+
0
|
|
73
|
+
);
|
|
74
|
+
//console.log('Selected files total size:', selectedSize);
|
|
75
|
+
const projectedUsage = $fileInputConfig.usedBytes + selectedSize;
|
|
76
|
+
const usageRatio = projectedUsage / $fileInputConfig.maxBytes;
|
|
77
|
+
|
|
78
|
+
if (usageRatio >= 0.95) {
|
|
79
|
+
addToast({
|
|
80
|
+
status: 'error',
|
|
81
|
+
message: `Upload blocked: storage limit almost reached (${Math.round(usageRatio * 100)}%). Please delete some files or free up space.`,
|
|
82
|
+
persistent: true
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
handleUpload();
|
|
87
|
+
}
|
|
48
88
|
</script>
|
|
49
89
|
|
|
50
90
|
|
|
51
91
|
<FileInput
|
|
52
92
|
minHeight="400px"
|
|
53
93
|
bind:processing={$fileInputStore.processing}
|
|
54
|
-
onclick={
|
|
94
|
+
onclick={handleStorageStatus}
|
|
55
95
|
/>
|
|
@@ -13,6 +13,7 @@ export type FileInputState = {
|
|
|
13
13
|
selectedFiles: File[];
|
|
14
14
|
sizeConstraint: number;
|
|
15
15
|
uploadType: Array<'image' | 'audio' | 'video' | 'documents' | 'others'>;
|
|
16
|
+
onUpload?: () => void;
|
|
16
17
|
activeTab: 'Cloud' | 'Upload';
|
|
17
18
|
activeMenu: 'Documents' | 'Music' | 'Pictures' | 'Videos' | 'Others';
|
|
18
19
|
disabledMenuItem: Array<'Music' | 'Documents' | 'Pictures' | 'Videos' | 'Others'> | null;
|
|
@@ -29,6 +30,7 @@ export type FileInputConfig = {
|
|
|
29
30
|
serverDownloadUrl: string;
|
|
30
31
|
usedBytes: number;
|
|
31
32
|
maxBytes: number;
|
|
33
|
+
user_id?: string;
|
|
32
34
|
};
|
|
33
35
|
export declare const fileInputConfig: import("svelte/store").Writable<FileInputConfig>;
|
|
34
36
|
export declare const fileInputStore: import("svelte/store").Writable<FileInputState>;
|
|
@@ -4,6 +4,7 @@ const defaultFileInputState = {
|
|
|
4
4
|
selectedFiles: [],
|
|
5
5
|
sizeConstraint: 10 * 1024 * 1024,
|
|
6
6
|
uploadType: ['image', 'audio', 'video', 'documents', 'others'],
|
|
7
|
+
onUpload: () => { },
|
|
7
8
|
// FilePicker
|
|
8
9
|
activeTab: 'Cloud',
|
|
9
10
|
activeMenu: 'Documents',
|
|
@@ -20,7 +21,8 @@ const defaultFileInputConfig = {
|
|
|
20
21
|
serverDeleteUrl: '',
|
|
21
22
|
serverDownloadUrl: '',
|
|
22
23
|
usedBytes: 0,
|
|
23
|
-
maxBytes: 104857600
|
|
24
|
+
maxBytes: 104857600,
|
|
25
|
+
user_id: '',
|
|
24
26
|
};
|
|
25
27
|
export const fileInputConfig = writable({
|
|
26
28
|
...defaultFileInputConfig
|