upload.js: moved the delete-an-attachment skeleton code to its own function
[citadel.git] / webcit-ng / static / js / upload.js
1 // Handle any tasks which require uploading files to the server (such as attachments)
2 // h/t https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/ which inspired the design of this module
3 //
4 // Copyright (c) 2016-2023 by the citadel.org team
5 //
6 // This program is open source software.  Use, duplication, or
7 // disclosure are subject to the GNU General Public License v3.
8
9 var uploads_in_progress = 0;
10 var uploads = [] ;                                                              // everything the user has uploaded
11
12
13 // Remove the upload window completely (even if it's hidden)
14 function deactivate_uploads() {
15         upload_window = document.getElementById('ctdl-upload');
16         if (upload_window) {
17                 upload_window.remove();
18         }
19 }
20
21
22 // Turn the specified div into a place where we can upload.  (Note: permanently changes the drag-and-drop behavior of that div.)
23 function activate_uploads(parent_div) {
24                 document.getElementById(parent_div).innerHTML += `
25                         <div class="ctdl-upload" id="ctdl-upload">
26                                 <div id="ctdl_attachments_title" class="ctdl-compose-attachments-title">
27                                         <div><h1><i class="fa fa-paperclip" style="color:grey"></i>` + _("Attachments:") + ` <span id="num_attachments">` + uploads.length + `</span></h1></div>
28                                         <div><h1><i class="fas fa-window-close" style="color:red" onClick="show_or_hide_upload_window()"></i></h1></div>
29                                 </div>
30                                 <br>
31                                 <ul id="ctdl-upload_list">
32                                 </ul>
33                                 <br>
34                                 <div id="drop-area" class="ctdl-upload-drop-area">
35                                         <form class="ctdl-upload-form">
36                                                 <p>${_("Drop files here to upload")}</p>
37                                                 <input type="file" id="fileElem" multiple accept="*/*" onChange="handle_upload_files(this.files)">
38                                         </form>
39                                 </div>
40                         </div>
41                 `;
42
43                 // activate drag and drop
44                 let dropArea = document.getElementById(parent_div);
45                 ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
46                         dropArea.addEventListener(eventName, upload_prevent_defaults, false)
47                 })
48                 ;["dragenter", "dragover"].forEach(eventName => {
49                         dropArea.addEventListener(eventName, upload_highlight, false)
50                 })
51                 ;['dragleave', 'drop'].forEach(eventName => {
52                         dropArea.addEventListener(eventName, upload_unhighlight, false)
53                 })
54                 dropArea.addEventListener('drop', upload_handle_drop, false);
55 }
56
57
58 // prevent drag and drop events from propagating up through the DOM
59 function upload_prevent_defaults(e) {
60         e.preventDefault();
61         e.stopPropagation();
62 }
63
64
65 function upload_handle_drop(e) {
66         let dt = e.dataTransfer;
67         let files = dt.files;
68         handle_upload_files(files);
69 }
70
71
72 function handle_upload_files(files) {
73         ([...files]).forEach(upload_file)
74 }
75
76
77 // Delete an uploaded item from the list
78 delete_upload = async(ref) => {
79
80         response = await fetch(
81                 "/ctdl/p/" + ref, { method: "DELETE" }
82         );
83
84         if (response.ok) {                              // If the server accepted the delete, remove it from the screen
85                 // FIXME do the delete
86         }
87 }
88
89
90 function upload_file(file) {
91         var url = '/ctdl/p/';
92         var xhr = new XMLHttpRequest();
93         var formData = new FormData();
94         xhr.open('POST', url, true);
95       
96         xhr.addEventListener('readystatechange', function(e) {
97                 if (xhr.readyState == 4 && xhr.status == 200) {
98                         // remove the "uploading in progress" message
99                         let li = document.getElementById("ctdl_uploading_" + uploads_in_progress.toString());
100                         li.parentNode.removeChild(li);
101                         uploads_in_progress -= 1;
102
103                         // The response body will be a JSON array of completed uploads.
104                         var j_response = JSON.parse(xhr.response);
105
106                         // Add these uploads to the displayed list
107                         j_response.forEach((item) => {
108                                 let new_upl = document.createElement("li");
109                                 // item["ref"] is what we need
110                                 new_upl.innerHTML = `<i class="fa-solid fa-circle-xmark" style="color:red" onClick="delete_upload('` + item["ref"] + `')"></i>`
111                                 + `&nbsp;`
112                                 + item["uploadfilename"] + " (" + item["contenttype"] + ", " + item["contentlength"] + " " + _("bytes") + ")";
113                                 document.getElementById("ctdl-upload_list").appendChild(new_upl);
114
115                                 // append it to the global list of uploads
116                                 uploads.push(item);
117                         });
118                         document.getElementById("num_attachments").innerHTML = uploads.length;
119                 }
120                 else if (xhr.readyState == 4 && xhr.status != 200) {
121                         // remove the "uploading in progress" message (there was an error, so just let it disappear)
122                         let li = document.getElementById("ctdl_uploading_" + uploads_in_progress.toString());
123                         li.parentNode.removeChild(li);
124                         uploads_in_progress -= 1;
125                 }
126         })
127  
128         formData.append('file', file);
129         xhr.send(formData);
130         uploads_in_progress += 1;
131
132         // Make an "uploading in progress" message appear in the uploads list!
133         progress = document.createElement("li");
134         progress.setAttribute("id", "ctdl_uploading_" + uploads_in_progress.toString());
135         progress.innerHTML = `<img src="/ctdl/s/images/dotcrawl.gif" /> ` + _("Processing dropped files...");
136         document.getElementById("ctdl-upload_list").appendChild(progress);
137 }
138
139
140 // called when the user drags a file into the upload area
141 function upload_highlight(e) {
142         let dropArea = document.getElementById("ctdl-upload");
143         dropArea.classList.add('highlight')
144
145         document.getElementById("ctdl-upload").style.display = "block";         /* also make it appear */
146 }
147
148
149 // called when the user is no longer dragging a file into the upload area
150 function upload_unhighlight(e) {
151         let dropArea = document.getElementById("ctdl-upload");
152         dropArea.classList.remove('highlight')
153 }
154
155
156 // Show or hide the attachments window in the composer
157 function show_or_hide_upload_window() {
158
159         if (document.getElementById("ctdl-upload").style.display == "block") {
160                 document.getElementById("ctdl-upload").style.display = "none";          /* turn it off */
161         }
162         else {
163                 document.getElementById("ctdl-upload").style.display = "block";         /* turn it on */
164         }
165 }
166
167
168 // Flush all uploaded files and close the window
169 function flush_uploads() {
170         upload_window = document.getElementById('ctdl-upload');
171
172         if (upload_window) {
173                 upload_window.style.display='none';
174         }
175
176         // FIXME tell the server to delete the files
177         uploads.forEach(u => {
178                 console.log(u.ref);
179         });
180         uploads=[];
181
182         deactivate_uploads();   // this makes the window get destroyed too
183 }