X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit-ng%2Fstatic%2Fjs%2Fview_mail.js;h=b6e09893ce5c32bbd8067c92923e0cfa46c947d3;hb=545fc1a847d9401923ef312f995d4f385da84e56;hp=2e05f7d6df7516643dadf8e0c33460613c33dde8;hpb=c229674219844f86c2a4a538bdfec997e624aca1;p=citadel.git diff --git a/webcit-ng/static/js/view_mail.js b/webcit-ng/static/js/view_mail.js index 2e05f7d6d..b6e09893c 100644 --- a/webcit-ng/static/js/view_mail.js +++ b/webcit-ng/static/js/view_mail.js @@ -3,7 +3,7 @@ // Copyright (c) 2016-2023 by the citadel.org team // // This program is open source software. Use, duplication, or -// disclosure are subject to the GNU General Public License v3. +// disclosure is subject to the GNU General Public License v3. var displayed_message = 0; // ID of message currently being displayed @@ -13,7 +13,6 @@ var newmail_notify = { NO : 0, // do not perform new mail notifications YES : 1 // yes, perform new mail notifications }; -var num_attachments = 0; // number of attachments in current composed msg // This is the async back end for mail_delete_selected() @@ -138,6 +137,9 @@ function mail_render_one(msgnum, msg, target_div, include_controls) { ; if (include_controls) { // omit controls if this is a pull quote + + let subject = msg.subj ? escapeJS(msg.subj) : "" ; + outmsg += render_userpic(msg.from) // user avatar + "
" // begin content @@ -150,16 +152,22 @@ function mail_render_one(msgnum, msg, target_div, include_controls) { + "" // end header info on left side + "" // begin buttons on right side - + "" // Reply (mail is always Quoted) - + "" + + "" // Reply + + `` + " " + _("Reply") + "" - + "" // Reply-All (mail is always Quoted) - + "" + + "" // Reply-All + + `` + " " + _("ReplyAll") + + "" + + + "" + + `` + + " " + + _("Forward") + ""; if (can_delete_messages) { @@ -208,8 +216,8 @@ function mail_render_one(msgnum, msg, target_div, include_controls) { "
"; // end header } - // Display attachments, if any are present - if (msg.part) { + // Display attachments, if any are present (don't do this if we're quoting the message) + if ( (msg.part) && (include_controls) ) { let display_attachments = 0; for (let r=0; r" + _("Write mail"); + document.getElementById("ctdl-newmsg-button").innerHTML = ` ` + _("Write mail"); document.getElementById("ctdl-newmsg-button").style.display = "block"; // Put the "delete message(s)" button into the topbar let d = document.getElementById("ctdl-delete-button"); - d.innerHTML = "" + _("Delete"); + d.innerHTML = ` ` + _("Delete"); d.style.display = "block"; //d.addEventListener("click", mail_delete_selected); - document.getElementById("ctdl-main").innerHTML - = "
" - + "
" - + "
" - + "
" - ; + document.getElementById("ctdl-main").innerHTML = ` +
+
+
+
+ `; - highest_mailnum = 0; // Keep track of highest message number to track newly arrived messages + highest_mailnum = 0; // Keep track of highest msg number to track newly arrived msgs render_mailbox_display(newmail_notify.NO); try { // if this was already set up, clear it so there aren't multiple clearInterval(RefreshMailboxInterval); @@ -373,9 +381,9 @@ function refresh_mail_display() { fetch_stat = async() => { response = await fetch(url); stat = await(response.json()); - if (stat.room_mtime > room_mtime) { // FIXME commented out to force refreshes + if (stat.room_mtime > room_mtime) { // if modified... room_mtime = stat.room_mtime; - render_mailbox_display(newmail_notify.YES); + render_mailbox_display(newmail_notify.YES); // ...force a refresh } } fetch_stat(); @@ -405,12 +413,14 @@ function render_mailbox_display(notify) { } // begin rendering the mailbox table - box = "" - + "" - + "" - + "" - + "" - + ""; + box = ` +
" + _("Subject") + "" + _("Sender") + "" + _("Date") + "#
+ + + + + + `; for (let i=0; i 0) { - m_to_str += ", "; - } - m_to_str += m_to[i].replaceAll("<", "<").replaceAll(">", ">"); +// helper function for mail_compose() -- converts a recipient array to a string suitable for the To: or Cc: field +function recipient_array_to_string(recps_arr) { + + let returned_string = "" + + if (recps_arr) { + is_reply = 1; + + // first clean up the recipients + for (i=0; i", ">"); } - } - else { - m_to_str = ""; - } - // m_to will be an array of zero or more recipients for the Cc: field. Convert it to a string. - if (m_cc) { - m_cc = Array.from(new Set(m_cc)); // remove dupes - m_cc_str = ""; - for (i=0; i 0) { - m_cc_str += ", "; + returned_string += ", "; } - m_cc_str += m_cc[i].replaceAll("<", "<").replaceAll(">", ">"); + returned_string += recps_arr[i]; } } - else { - m_cc_str = ""; - } + + return(returned_string); +} + + + +// Compose a new mail message (called by the Reply button here, or by the dispatcher in views.js) +// +// references list of references, be sure to use this in a reply +// quoted_msgnum if a reply, the msgid of the most recent message in the chain, the one to which we are replying +// (set to 0 if this is not a reply) +// m_to an ARRAY of zero or more recipients to pre-insert into the To: field +// m_cc an ARRAY of zero or more recipients to pre-insert into the Cc: field +// m_subject a string to pre-insert into the Subject: field +// +function mail_compose(references, quoted_msgnum, m_to, m_cc, m_subject) { + + let is_reply = 0; + let is_quoted = (quoted_msgnum > 0) ? true : false ; + let is_fwd = (is_quoted && m_to.length==0 && m_cc.length==0) ; + + // m_to will be an array of zero or more recipients for the To: field. Convert it to a string. + m_to_str = recipient_array_to_string(m_to); + + // m_cc will be an array of zero or more recipients for the Cc: field. Convert it to a string. + m_cc_str = recipient_array_to_string(m_cc); quoted_div_name = randomString(); // Make the "Write mail" button disappear. We're already there! document.getElementById("ctdl-newmsg-button").style.display = "none"; - // is_quoted true or false depending on whether the user selected "reply quoted" (is this appropriate for mail?) - // references list of references, be sure to use this in a reply - // msgid if a reply, the msgid of the most recent message in the chain, the one to which we are replying - - // Now display the screen. (Yes, I combined regular strings + template literals. I just learned template literals. Converting to all template literals would be fine.) + // Now display the screen. (Yes, I combined regular strings + template literals. + // I just learned template literals. Converting the whole thing to template literals would be fine.) compose_screen = // Hidden values that we are storing right here in the document tree for later - "" - + "" + "
" + references + "
" // Header fields, the composition window, and the button bar are arranged using a Grid layout. + "
" @@ -503,8 +529,12 @@ function mail_compose(is_quoted, references, quoted_msgnum, m_to, m_cc, m_subjec + "
" ; - if (is_quoted) { - compose_screen += "

"; + // If this is a quoted reply, insert a div within which we will render the original message. + if (is_quoted && is_fwd) { + compose_screen += "

" + _("--- forwarded message ---") + "
QUOTE
"; + } + else if (is_quoted) { + compose_screen += "

QUOTE
"; } // The button bar is a Grid element, and is also a Flexbox container. @@ -513,137 +543,48 @@ function mail_compose(is_quoted, references, quoted_msgnum, m_to, m_cc, m_subjec
${_("Send message")} ${_("Save to Drafts")} - ${_("Attachments:")} ${num_attachments} + ${_("Attachments:")} ${uploads.length} ${_("Contacts")} - ${_("Cancel")} + ${_("Cancel")}
` ; + document.getElementById("ctdl-main").innerHTML = compose_screen; - mail_display_message(quoted_msgnum, document.getElementById(quoted_div_name), 0); + if (m_cc) { document.getElementById("ctdl-compose-cc-label").style.display = "block"; document.getElementById("ctdl-compose-cc-field").style.display = "block"; } - activate_uploads(); -} - - -function activate_uploads() { - document.getElementById("ctdl_big_modal").innerHTML = ` -
-
-

` + _("Attachments:") + " " + num_attachments + `

-

-
-
-
    -
  • uploaded file
  • -
  • another uploaded file
  • -
  • philez and warez
  • -
-
-
-
-

${_("Drop files here to upload")}

- - - -
-
- `; - - // activate drag and drop (shamelessly swiped from https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/ ) - let dropArea = document.getElementById("drop-area"); - ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { - dropArea.addEventListener(eventName, preventDefaults, false) - }) - ;["dragenter", "dragover"].forEach(eventName => { - dropArea.addEventListener(eventName, highlight, false) - }) - ;['dragleave', 'drop'].forEach(eventName => { - dropArea.addEventListener(eventName, unhighlight, false) - }) - dropArea.addEventListener('drop', handleDrop, false); - - // make the modal smaller than the containing window but pretty big - document.getElementById("ctdl_attachments_outer").style.width = - Math.trunc((document.getElementById("ctdl-editor-body").getBoundingClientRect().width) * 0.60).toString() + "px"; - document.getElementById("ctdl_attachments_outer").style.height = - Math.trunc((document.getElementById("ctdl-editor-body").getBoundingClientRect().height) * 0.60).toString() + "px"; -} - - -// prevent drag and drop events from propagating up through the DOM -function preventDefaults(e) { - e.preventDefault(); - e.stopPropagation(); -} - - -function handleDrop(e) { - let dt = e.dataTransfer; - let files = dt.files; - handleFiles(files); -} - - -function handleFiles(files) { - ([...files]).forEach(uploadFile) -} - - -function uploadFile(file) { - var url = '/ctdl/zzz/attach_it;' - var xhr = new XMLHttpRequest(); - var formData = new FormData(); - xhr.open('POST', url, true); - - xhr.addEventListener('readystatechange', function(e) { - if (xhr.readyState == 4 && xhr.status == 200) { - document.getElementById("ctdl_upload_list").innerHTML += "
  • succeeeeed
  • "; - console.log("upload succeeded"); - } - else if (xhr.readyState == 4 && xhr.status != 200) { - document.getElementById("ctdl_upload_list").innerHTML += "
  • EPIC FAIL
  • "; - console.log("upload failed"); - } - }) - - formData.append('file', file); - xhr.send(formData); -} - - -function highlight(e) { - let dropArea = document.getElementById("drop-area"); - dropArea.classList.add('highlight') -} - -function unhighlight(e) { - let dropArea = document.getElementById("drop-area"); - dropArea.classList.remove('highlight') -} - + activate_uploads("ctdl-editor-body"); // create the attachments window + attachment_counter_divs.push("ctdl_num_attachments"); // make the Attachments: count at the bottom update too + // If this is a quoted reply, render the original message into the div we set up earlier. + if (is_quoted) { + mail_display_message(quoted_msgnum, document.getElementById(quoted_div_name), 0); + } -// Show or hide the attachments window in the composer -function show_or_hide_attachments() { + // If this is a forwarded messages, preload its attachments into the forwarded copy. + if (is_fwd) { + forward_attachments(quoted_msgnum); + } - if (document.getElementById("ctdl_big_modal").style.display == "block") { - document.getElementById("ctdl_big_modal").style.display = "none"; + if (is_reply) { + setTimeout(() => { document.getElementById("ctdl-editor-body").focus(); }, 0); } else { - document.getElementById("ctdl_big_modal").style.display = "block"; + setTimeout(() => { document.getElementById("ctdl-compose-to-field").focus(); }, 0); } -} +} // Called when the user clicks the button to make the hidden "CC" and "BCC" lines appear. // It is also called automatically during a Reply when CC is pre-populated. function make_cc_bcc_visible() { document.getElementById("ctdl-cc-bcc-buttons").style.display = "none"; + document.getElementById("ctdl-compose-cc-label").style.display = "block"; + document.getElementById("ctdl-compose-cc-field").style.display = "block"; document.getElementById("ctdl-compose-bcc-label").style.display = "block"; document.getElementById("ctdl-compose-bcc-field").style.display = "block"; } @@ -665,29 +606,29 @@ function msm_field(element_name, separator) { function mail_send_message() { document.body.style.cursor = "wait"; + deactivate_uploads(); let url = "/ctdl/r/" + escapeHTMLURI(current_room) + "/dummy_name_for_new_mail" - + "?wefw=" + msm_field("ctdl_mc_references", "!") // references (if present) + + "?wefw=" + msm_field("ctdl-mc-references", "!") // references (if present) + "&subj=" + msm_field("ctdl-compose-subject-field", " ") // subject (if present) + "&mailto=" + msm_field("ctdl-compose-to-field", ",") // To: (required) + "&mailcc=" + msm_field("ctdl-compose-cc-field", ",") // Cc: (if present) + "&mailbcc=" + msm_field("ctdl-compose-bcc-field", ",") // Bcc: (if present) ; - boundary = randomString(); - body_text = - "--" + boundary + "\r\n" - + "Content-type: text/html\r\n" - + "Content-transfer-encoding: quoted-printable\r\n" - + "\r\n" - + quoted_printable_encode( - "" + document.getElementById("ctdl-editor-body").innerHTML + "" - ) + "\r\n" - + "--" + boundary + "--\r\n" - ; + if (uploads.length > 0) { + url += "&att="; + for (let i=0; i\r\n"; var request = new XMLHttpRequest(); request.open("PUT", url, true); - request.setRequestHeader("Content-type", "multipart/mixed; boundary=\"" + boundary + "\""); + request.setRequestHeader("Content-type", "text/html"); request.onreadystatechange = function() { if (request.readyState == 4) { document.body.style.cursor = "default"; @@ -699,6 +640,9 @@ function mail_send_message() { } } + // successfully saving a message means the attachments are now gone from the server. + uploads = []; + // After saving the message, go back to the mailbox view. gotoroom(current_room);
    ${_("Subject")}${ _("Sender")}${_("Date")}#