Browse Source

Add checkboxes to mail list

- Add checkboxes to mail list and basic actions (no backend yet)
- Unify quotes in js
Alexey Edelev 5 years ago
parent
commit
21bb9a5484

File diff suppressed because it is too large
+ 0 - 0
web/assets/back.svg


+ 1 - 1
web/assets/logout.svg

@@ -28,7 +28,7 @@
   <defs
      id="defs10" />
   <path
-     fill="#41cd52"
+     fill="#3ab949"
      d="M 27.83,16.83 25,14 15,24 25,34 27.83,31.17 22.66,26 H 42 V 22 H 22.66 Z M 10,42 h 28 c 2.21,0 4,-1.79 4,-4 v -8 h -4 v 8 H 10 V 10 h 28 v 8 h 4 V 10 C 42,7.79 40.21,6 38,6 H 10 C 7.79,6 6,7.79 6,10 v 28 c 0,2.21 1.79,4 4,4 z"
      id="path4"
      inkscape:connector-curvature="0"

File diff suppressed because it is too large
+ 0 - 0
web/assets/read.svg


File diff suppressed because it is too large
+ 0 - 0
web/assets/remove.svg


File diff suppressed because it is too large
+ 0 - 0
web/assets/restore.svg


File diff suppressed because it is too large
+ 0 - 0
web/assets/settings.svg


File diff suppressed because it is too large
+ 0 - 0
web/assets/unread.svg


+ 156 - 1
web/css/controls.css

@@ -79,6 +79,7 @@
     background-color: var(--inactive-color);
 }
 
+/* faders */
 .fadeOut {
     background: -moz-linear-gradient(top, rgba(0, 0, 0, 0) 0%, var(--bg-color) 100%); /* FF3.6+ */
     background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0, 0, 0,0)), color-stop(100%,var(--bg-color))); /* Chrome,Safari4+ */
@@ -259,21 +260,33 @@
     -moz-user-select: none;
     -ms-user-select: none;
     user-select: none;
+
+    transition: background-color .2s ease-out;;
 }
 
 .folderBtn:hover {
     cursor: pointer;
     background-color: var(--secondary-color);
+
+    transition: background-color .2s ease-out;;
 }
 
+/* icon button */
+
 .iconBtn:hover, .iconBtn:focus {
     cursor: pointer;
+
+    opacity: 1.0;
+    transition: opacity .2s ease-out;
 }
 
 .iconBtn {
     width: 24px;
     min-width: 24px;
 
+    opacity: 0.75;
+    transition: opacity .2s ease-out;
+
     -webkit-touch-callout: none;
     -webkit-user-select: none;
     -khtml-user-select: none;
@@ -393,4 +406,146 @@
 .toast.critical {
     background-color: var(--bad-color);
     color: var(--secondary-text-color);
-}
+}
+
+/* checkbox */
+
+.cbox {
+    z-index: 0;
+    position: relative;
+    display: inline-block;
+    color: rgba(0, 0, 0, 0.87);
+    font-size: 16px;
+    line-height: 1.5;
+}
+
+/* Input */
+.cbox > input {
+    appearance: none;
+    -moz-appearance: none;
+    -webkit-appearance: none;
+    z-index: -1;
+    position: absolute;
+    left: -10px;
+    top: -8px;
+    display: block;
+    margin: 0;
+    border-radius: 50%;
+    width: 40px;
+    height: 40px;
+    background-color: rgba(0, 0, 0, 0.6);
+    box-shadow: none;
+    outline: none;
+    opacity: 0;
+    transform: scale(1);
+    pointer-events: none;
+    transition: opacity 0.3s, transform 0.2s;
+}
+
+/* Span */
+.cbox > span {
+    display: inline-block;
+    width: 100%;
+    cursor: pointer;
+}
+
+/* Box */
+.cbox > span::before {
+    content: "";
+    display: inline-block;
+    box-sizing: border-box;
+    margin: 3px 11px 3px 1px;
+    border: solid 2px; /* Safari */
+    border-color: rgba( 0, 0, 0, 0.6);
+    border-radius: 2px;
+    width: 18px;
+    height: 18px;
+    vertical-align: top;
+    transition: border-color 0.2s, background-color 0.2s;
+}
+
+/* Checkmark */
+.cbox > span::after {
+    content: "";
+    display: block;
+    position: absolute;
+    top: 3px;
+    left: 1px;
+    width: 10px;
+    height: 5px;
+    border: solid 2px transparent;
+    border-right: none;
+    border-top: none;
+    transform: translate(3px, 4px) rotate(-45deg);
+}
+
+/* Checked, Indeterminate */
+.cbox > input:checked,
+.cbox > input:indeterminate {
+    background-color: var(--primary-color);
+}
+
+.cbox > input:checked + span::before,
+.cbox > input:indeterminate + span::before {
+    border-color: var(--primary-color);
+    background-color: var(--primary-color);
+}
+
+.cbox > input:checked + span::after,
+.cbox > input:indeterminate + span::after {
+    border-color: var(--bg-color);
+}
+
+.cbox > input:indeterminate + span::after {
+    border-left: none;
+    transform: translate(4px, 3px);
+}
+
+/* Hover, Focus */
+.cbox:hover > input {
+    opacity: 0.04;
+}
+
+.cbox > input:focus {
+    opacity: 0.12;
+}
+
+.cbox:hover > input:focus {
+    opacity: 0.16;
+}
+
+/* Active */
+.cbox > input:active {
+    opacity: 1;
+    transform: scale(0);
+    transition: transform 0s, opacity 0s;
+}
+
+.cbox > input:active + span::before {
+    border-color: var(--primary-color);
+}
+
+.cbox > input:checked:active + span::before {
+    border-color: transparent;
+    background-color: rgba(0, 0, 0, 0.6);
+}
+
+/* Disabled */
+.cbox > input:disabled {
+    opacity: 0;
+}
+
+.cbox > input:disabled + span {
+    color: rgba(0, 0, 0, 0.38);
+    cursor: initial;
+}
+
+.cbox > input:disabled + span::before {
+    border-color: currentColor;
+}
+
+.cbox > input:checked:disabled + span::before,
+.cbox > input:indeterminate:disabled + span::before {
+    border-color: transparent;
+    background-color: currentColor;
+}

+ 6 - 0
web/css/index.css

@@ -161,6 +161,12 @@ html, body {
     justify-content: flex-end;
 }
 
+#multiActions {
+    display: none;
+    flex-direction: row;
+    margin-right: 30px;
+}
+
 .verticalPaddingBox {
     display: block;
     flex: 1 1 auto;

+ 3 - 3
web/css/styles.css

@@ -6,12 +6,12 @@
 
 :root {
     --primary-color: #41cd52;
-    --primary-dark-color: #3ab849;
-    --secondary-color: #ebffee;
+    --primary-dark-color: #3ab949;
+    --secondary-color: #e6ffe9;
     --secondary-dark-color: #b8d4bc;
     --inactive-color: #cfcfcf;
     --bg-color: #ffffff;
-    --bg-dark-color: #ebffee;
+    --bg-dark-color: #e6ffe9;
     --primary-text-color: #000000;
     --primary-text-dark-color: #000000;
     --secondary-text-color: #ffffff;

+ 8 - 8
web/js/forms.js

@@ -39,9 +39,9 @@ function validatePassword(name, form) {
     if (element.val() != '') {
         fieldDiv.removeClass('bad')
         if (element.val().length < 8 || !passwordRegex.test(element.val())) {
-            fieldDiv.addClass("weak")
+            fieldDiv.addClass('weak')
         } else {
-            fieldDiv.removeClass("weak")
+            fieldDiv.removeClass('weak')
         }
     } else {
         fieldDiv.removeClass('weak')
@@ -54,9 +54,9 @@ function validateFullName(name, form) {
     var element = $(name)
     var fieldDiv = element.parent()
     if (fullNameRegex.test(element.val())) {
-        fieldDiv.removeClass("bad")
+        fieldDiv.removeClass('bad')
     } else {
-        fieldDiv.addClass("bad")
+        fieldDiv.addClass('bad')
     }
     validateForm(form)
 }
@@ -77,21 +77,21 @@ var emailInputTimer = null
 function validateEmail(name, form) {
     var element = $(name)
     var fieldDiv = element.parent()
-    fieldDiv.addClass("bad")
+    fieldDiv.addClass('bad')
     if (emailRegex.test(element.val())) {
         clearTimeout(emailInputTimer)
         emailInputTimer = setTimeout(function(){
                 $.ajax({
-                url: "/checkEmail",
+                url: '/checkEmail',
                 data: {
                     user: element.val()
                 },
                 success: function(result) {
-                    fieldDiv.removeClass("bad")
+                    fieldDiv.removeClass('bad')
                     validateForm(form)
                 },
                 error: function(jqXHR, textStatus, errorThrown) {
-                    fieldDiv.addClass("bad")
+                    fieldDiv.addClass('bad')
                     validateForm(form)
                 }
             })

+ 141 - 118
web/js/index.js

@@ -23,10 +23,10 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-var currentFolder = ""
+var currentFolder = ''
 var currentPage = 0
-var currentMail = ""
-var mailbox = ""
+var currentMail = ''
+var mailbox = ''
 var pageMax = 10
 const mailboxRegex = /^(\/m\d+)/g
 const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
@@ -43,27 +43,27 @@ $(window).click(function(e){
     var target = $(e.target)
     var isDropDown = false
     for (var i = 0; i < target.parents().length; i++) {
-        isDropDown = target.parents()[i].classList.contains("dropbtn")
+        isDropDown = target.parents()[i].classList.contains('dropbtn')
         if (isDropDown) {
             break
         }
     }
     if (!e.target.matches('.dropbtn') && !isDropDown) {
-        $(".dropdown-content").hide()
+        $('.dropdown-content').hide()
     }
 })
 
 $(document).ready(function(){
     $.ajaxSetup({
         global: false,
-        type: "POST"
+        type: 'POST'
     })
 
     urlPaths = mailboxRegex.exec($(location).attr('pathname'))
     if (urlPaths != null && urlPaths.length === 2) {
         mailbox = urlPaths[0]
     } else {
-        mailbox = ""
+        mailbox = ''
     }
 
     $(window).bind('hashchange', onHashChanged)
@@ -71,24 +71,24 @@ $(document).ready(function(){
     loadFolders()
     loadStatusLine()
 
-    $("#mailNewButton").click(mailNew)
+    $('#mailNewButton').click(mailNew)
     connectNotifier()
 
-    $("#toEmailField").on("input", toEmailFieldChanged)
-    $("#toEmailField").keydown(function(e){
-        var actualText = $("#toEmailField").val()
+    $('#toEmailField').on('input', toEmailFieldChanged)
+    $('#toEmailField').keydown(function(e){
+        var actualText = $('#toEmailField').val()
         const selectionPosition = e.target.selectionStart
         switch(e.keyCode) {
             case 8:
                 if (toEmailPreviousSelectionPosition == 0 && e.target.selectionStart == 0
-                    && toEmailList.length > 0 && $("#toEmailList").children().length > 1) {
-                        removeToEmail($("#toEmailList").children()[$("#toEmailList").children().length - 2].id, toEmailList[toEmailList.length - 1])
+                    && toEmailList.length > 0 && $('#toEmailList').children().length > 1) {
+                        removeToEmail($('#toEmailList').children()[$('#toEmailList').children().length - 2].id, toEmailList[toEmailList.length - 1])
                     }
             break
             case 13:
             case 9:
                 addToEmail(actualText.slice(0, selectionPosition))
-                $("#toEmailField").val(actualText.slice(selectionPosition + 1, actualText.length))
+                $('#toEmailField').val(actualText.slice(selectionPosition + 1, actualText.length))
                 break
         }
         toEmailPreviousSelectionPosition = e.target.selectionStart
@@ -97,7 +97,7 @@ $(document).ready(function(){
 
 function toEmailFieldChanged(e) {
     const selectionPosition = e.target.selectionStart - 1
-    var actualText = $("#toEmailField").val()
+    var actualText = $('#toEmailField').val()
 
     if (actualText.length <= 0 || selectionPosition < 0) {
         return
@@ -106,7 +106,7 @@ function toEmailFieldChanged(e) {
     var lastChar = actualText[selectionPosition]
     if (emailEndRegex.test(lastChar)) {
         addToEmail(actualText.slice(0, selectionPosition))
-        $("#toEmailField").val(actualText.slice(selectionPosition + 1, actualText.length))
+        $('#toEmailField').val(actualText.slice(selectionPosition + 1, actualText.length))
     }
 }
 
@@ -114,8 +114,8 @@ function addToEmail(toEmail) {
     if (toEmail.length <= 0) {
         return
     }
-    var style = emailRegex.test(toEmail) ? "valid" : "invalid"
-    $("<div class=\""+ style + " toEmail\" id=\"toEmail" + toEmailIndex + "\">" + toEmail + "<img class=\"iconBtn\" style=\"height: 12px; margin-left:10px; margin: auto;\" onclick=\"removeToEmail('toEmail" + toEmailIndex + "', '" + toEmail + "');\" src=\"/assets/cross.svg\"/></div>").insertBefore("#toEmailField")
+    var style = emailRegex.test(toEmail) ? 'valid' : 'invalid'
+    $('<div class="'+ style + ' toEmail" id="toEmail' + toEmailIndex + '">' + toEmail + '<img class="iconBtn" style="height: 12px; margin-left:10px; margin: auto;" onclick="removeToEmail(\'toEmail' + toEmailIndex + '\', \'' + toEmail + '\');" src="/assets/cross.svg"/></div>').insertBefore('#toEmailField')
     toEmailIndex++
     toEmailList.push(toEmail)
     checkSendDisabled()
@@ -128,23 +128,23 @@ function removeToEmail(id, email) {
         checkSendDisabled()
     }
 
-    $("#" + id).remove()
+    $('#' + id).remove()
 }
 
 function checkSendDisabled() {
     if (toEmailList.length > 0) {
-        $("#sendButton").removeClass("disabled")
+        $('#sendButton').removeClass('disabled')
     } else {
-        $("#sendButton").addClass("disabled")
+        $('#sendButton').addClass('disabled')
     }
 }
 
 function mailNew(e) {
-    window.location.hash = currentFolder + currentPage + "/mailNew"
+    window.location.hash = currentFolder + currentPage + '/mailNew'
 }
 
 function mailOpen(id) {
-    window.location.hash = currentFolder + currentPage + "/" + id
+    window.location.hash = currentFolder + currentPage + '/' + id
 }
 
 function openFolder(folder) {
@@ -155,7 +155,7 @@ function onHashChanged() {
     var hashLocation = window.location.hash
     if (hashLocation == "") {
         setDetailsVisible(false)
-        openFolder("Inbox")
+        openFolder('Inbox')
         return
     }
 
@@ -164,7 +164,7 @@ function onHashChanged() {
     page = 0
     if (hashParts.length >= 3 && hashParts[2] != "") {
         page = parseInt(hashParts[2])
-        if (typeof page != "number" || page > pageMax || page < 0) {
+        if (typeof page != 'number' || page > pageMax || page < 0) {
             page = 0
         }
     }
@@ -173,7 +173,7 @@ function onHashChanged() {
         updateMailList(hashParts[1], page)
     }
 
-    if (hashParts.length >= 4 && hashParts[3] != "" && hashParts[3] != "/mailNew") {
+    if (hashParts.length >= 4 && hashParts[3] != "" && hashParts[3] != '/mailNew') {
         if (currentMail != hashParts[3]) {
             requestMail(hashParts[3])
         }
@@ -181,8 +181,8 @@ function onHashChanged() {
         setDetailsVisible(false)
     }
 
-    hashParts = hashLocation.split("/")
-    if (hashParts.length == 2 && hashParts[1] == "mailNew") {
+    hashParts = hashLocation.split('/')
+    if (hashParts.length == 2 && hashParts[1] == 'mailNew') {
         setMailNewVisible(true)
     } else {
         setMailNewVisible(false)
@@ -192,70 +192,70 @@ function onHashChanged() {
 function requestMail(mailId) {
     if (mailId != "") {
         $.ajax({
-            url: "/mail",
+            url: '/mail',
             data: {
                 mailId: mailId
             },
             success: function(result) {
                 currentMail = mailId
-                if ($("#readListIcon"+mailId)) {
-                    $("#readListIcon"+mailId).attr("src", "/assets/read.svg")
+                if ($('#readListIcon'+mailId)) {
+                    $('#readListIcon'+mailId).attr('src', '/assets/read.svg')
                 }
-                $("#mail"+mailId).removeClass("unread")
-                $("#mail"+mailId).addClass("read")
-                $("#mailDetails").html(result);
+                $('#mail'+mailId).removeClass('unread')
+                $('#mail'+mailId).addClass('read')
+                $('#mailDetails').html(result);
                 setDetailsVisible(true);
                 folderStat(currentFolder);//TODO: receive statistic from websocket
             },
             error: function(jqXHR, textStatus, errorThrown) {
-                $("#mailDetails").html(textStatus)
+                $('#mailDetails').html(textStatus)
                 setDetailsVisible(true)
-                showToast(Severity.Critical, "Unable to open mail: " + errorThrown + " " + textStatus)
+                showToast(Severity.Critical, 'Unable to open mail: ' + errorThrown + ' ' + textStatus)
             }
         })
     }
 }
 
 function loadFolders() {
-    if (mailbox == "") {
-        $("#folders").html("Unable to load folder list")
+    if (mailbox == '') {
+        $('#folders').html('Unable to load folder list')
         return
     }
 
     $.ajax({
-        url: mailbox + "/folders",
+        url: mailbox + '/folders',
         success: function(result) {
             folderList = jQuery.parseJSON(result)
             for(var i = 0; i < folderList.folders.length; i++) {
                 folders.push(folderList.folders[i].name)
                 folderStat(folderList.folders[i].name)
             }
-            $("#folders").html(folderList.html)
+            $('#folders').html(folderList.html)
         },
         error: function(jqXHR, textStatus, errorThrown) {
-            showToast(Severity.Critical, "Unable to update folder list: " + errorThrown + " " + textStatus)
+            showToast(Severity.Critical, 'Unable to update folder list: ' + errorThrown + ' ' + textStatus)
         }
     })
 }
 
 function folderStat(folder) {
     $.ajax({
-        url: mailbox + "/folderStat",
+        url: mailbox + '/folderStat',
         data: {
             folder: folder
         },
         success: function(result) {
             var stats = jQuery.parseJSON(result)
             if (stats.unread > 0) {
-                $("#folderStats"+folder).text(stats.unread)
-                $("#folder"+folder).addClass("unread")
+                $('#folderStats'+folder).text(stats.unread)
+                $('#folder'+folder).addClass('unread')
             } else {
-                $("#folder"+folder).removeClass("unread")
-                $("#folderStats"+folder).text("")
+                $('#folder'+folder).removeClass('unread')
+                $('#folderStats'+folder).text("")
             }
         },
         error: function(jqXHR, textStatus, errorThrown) {
-            showToast(Severity.Critical, "Unable to update folder list: " + errorThrown + " " + textStatus)
+            showToast(Severity.Critical, 'Unable to update folder list: ' + errorThrown + ' ' + textStatus)
         }
     })
 }
@@ -270,12 +270,12 @@ function closeMailNew() {
 
 function loadStatusLine() {
     $.ajax({
-        url: mailbox + "/statusLine",
+        url: mailbox + '/statusLine',
         success: function(result) {
-            $("#statusLine").html(result)
+            $('#statusLine').html(result)
         },
         error: function(jqXHR, textStatus, errorThrown) {
-            showToast(Severity.Critical, "Unable to load status line: " + errorThrown + " " + textStatus)
+            showToast(Severity.Critical, 'Unable to load status line: ' + errorThrown + ' ' + textStatus)
         }
     })
 }
@@ -296,33 +296,33 @@ function localDate(elementToChange, timestamp) {
         dateString = date.toLocaleDateString("en-US")
     }
 
-    $("#"+elementToChange).text(dateString)
+    $('#'+elementToChange).text(dateString)
 }
 
 function setRead(mailId, read) {
     $.ajax({
-        url: "/setRead",
+        url: '/setRead',
         data: {mailId: mailId,
                read: read},
         success: function(result) {
             if (read) {
-                if ($("#readIcon"+mailId)) {
-                    $("#readIcon"+mailId).attr("src", "/assets/read.svg")
+                if ($('#readIcon'+mailId)) {
+                    $('#readIcon'+mailId).attr('src', '/assets/read.svg')
                 }
-                if ($("#readListIcon"+mailId)) {
-                    $("#readListIcon"+mailId).attr("src", "/assets/read.svg")
+                if ($('#readListIcon'+mailId)) {
+                    $('#readListIcon'+mailId).attr('src', '/assets/read.svg')
                 }
-                $("#mail"+mailId).removeClass("unread")
-                $("#mail"+mailId).addClass("read")
+                $('#mail'+mailId).removeClass('unread')
+                $('#mail'+mailId).addClass('read')
             } else {
-                if ($("#readIcon"+mailId)) {
-                    $("#readIcon"+mailId).attr("src", "/assets/unread.svg")
+                if ($('#readIcon'+mailId)) {
+                    $('#readIcon'+mailId).attr('src', '/assets/unread.svg')
                 }
-                if ($("#readListIcon"+mailId)) {
-                    $("#readListIcon"+mailId).attr("src", "/assets/unread.svg")
+                if ($('#readListIcon'+mailId)) {
+                    $('#readListIcon'+mailId).attr('src', '/assets/unread.svg')
                 }
-                $("#mail"+mailId).removeClass("read")
-                $("#mail"+mailId).addClass("unread")
+                $('#mail'+mailId).removeClass('read')
+                $('#mail'+mailId).addClass('unread')
             }
             folderStat(currentFolder);//TODO: receive statistic from websocket
         },
@@ -332,38 +332,35 @@ function setRead(mailId, read) {
 }
 
 function toggleRead(mailId, iconId) {
-    if ($("#"+iconId+mailId)) {
-        setRead(mailId, $("#"+iconId+mailId).attr("src") == "/assets/unread.svg")
-    }
+    setRead(mailId, $('#' + iconId + mailId).attr('src') == '/assets/unread.svg')
 }
 
 function removeMail(mailId, callback) {
-    var url = currentFolder != "Trash" ? "/remove" : "/delete"
+    var url = currentFolder != 'Trash' ? '/remove' : '/delete'
     $.ajax({
         url: url,
         data: {mailId: mailId},
         success: function(result) {
-            $("#mail"+mailId).remove();
+            $('#mail'+mailId).remove();
             if (callback) {
                 callback();
             }
             folderStat(currentFolder);//TODO: receive statistic from websocket
-            folderStat("Trash");//TODO: receive statistic from websocket
+            folderStat('Trash');//TODO: receive statistic from websocket
         },
         error: function(jqXHR, textStatus, errorThrown) {
         }
     })
 }
 
-
 function restoreMail(mailId, callback) {
-    var url = "/restore"
+    var url = '/restore'
     $.ajax({
         url: url,
         data: {mailId: mailId},
         success: function(result) {
-            if (currentFolder == "Trash") {
-                $("#mail"+mailId).remove();
+            if (currentFolder == 'Trash') {
+                $('#mail'+mailId).remove();
             }
             if (callback) {
                 callback();
@@ -379,46 +376,46 @@ function restoreMail(mailId, callback) {
 
 function setDetailsVisible(visible) {
     if (visible) {
-        $("#mailDetails").show()
-        $("#mailList").css({pointerEvents: "none"})
+        $('#mailDetails').show()
+        $('#mailList').css({pointerEvents: 'none'})
     } else {
-        currentMail = ""
-        $("#mailDetails").hide()
-        $("#mailDetails").html("")
-        $("#mailList").css({pointerEvents: "auto"})
+        currentMail = ''
+        $('#mailDetails').hide()
+        $('#mailDetails').html('')
+        $('#mailList').css({pointerEvents: 'auto'})
     }
 }
 
 function setMailNewVisible(visible) {
     if (visible) {
-        $("#mailNew").show()
-        $("#mailList").css({pointerEvents: "none"})
+        $('#mailNew').show()
+        $('#mailList').css({pointerEvents: 'none'})
     } else {
-        currentMail = ""
-        $("#mailNew").hide()
-        $("#mailList").css({pointerEvents: "auto"})
+        currentMail = ''
+        $('#mailNew').hide()
+        $('#mailList').css({pointerEvents: 'auto'})
     }
 
-    while (toEmailList.length > 0 && $("#toEmailList").children().length > 1) {
-        removeToEmail($("#toEmailList").children()[$("#toEmailList").children().length - 2].id, toEmailList[toEmailList.length - 1])
+    while (toEmailList.length > 0 && $('#toEmailList').children().length > 1) {
+        removeToEmail($('#toEmailList').children()[$('#toEmailList').children().length - 2].id, toEmailList[toEmailList.length - 1])
     }
     toEmailList = new Array()
-    $("#newMailEditor").val("")
-    $("#newMailSubject").val("")
-    $("#newMailTo").val("")
-    $("#toEmailField").val("")
+    $('#newMailEditor').val('')
+    $('#newMailSubject').val('')
+    $('#newMailTo').val('')
+    $('#toEmailField').val('')
 }
 
 function updateMailList(folder, page) {
-    if (mailbox == "" || folder == "") {
-        if ($("#mailList")) {
-            $("#mailList").html("Unable to load message list")
+    if (mailbox == '' || folder == '') {
+        if ($('#mailList')) {
+            $('#mailList').html('Unable to load message list')
         }
         return
     }
 
     $.ajax({
-        url: mailbox + "/mailList",
+        url: mailbox + '/mailList',
         data: {
             folder: folder,
             page: page
@@ -427,22 +424,22 @@ function updateMailList(folder, page) {
             var data = jQuery.parseJSON(result)
             pageMax = Math.floor(data.total/50)
 
-            if ($("#mailList")) {
-                $("#mailList").html(data.html)
+            if ($('#mailList')) {
+                $('#mailList').html(data.html)
             }
             currentFolder = folder
             currentPage = page
 
-            if($("#currentPageIndex")) {
-                $("#currentPageIndex").text(currentPage + 1)
+            if($('#currentPageIndex')) {
+                $('#currentPageIndex').text(currentPage + 1)
             }
-            if($("#totalPageCount")) {
-                $("#totalPageCount").text(pageMax + 1)
+            if($('#totalPageCount')) {
+                $('#totalPageCount').text(pageMax + 1)
             }
         },
         error: function(jqXHR, textStatus, errorThrown) {
-            if ($("#mailList")) {
-                $("#mailList").html("Unable to load message list")
+            if ($('#mailList')) {
+                $('#mailList').html('Unable to load message list')
             }
         }
     })
@@ -459,7 +456,7 @@ function prevPage() {
 }
 
 function toggleDropDown(dd) {
-    $("#"+dd).toggle()
+    $('#'+dd).toggle()
 }
 
 function sendNewMail(force) {
@@ -476,30 +473,30 @@ function sendNewMail(force) {
     for(var i = 1; i < toEmailList.length; i++) {
         composedEmailString += "," + toEmailList[i]
     }
-    $("#newMailTo").val(composedEmailString)
-    var formValue = $("#mailNewForm").serialize()
+    $('#newMailTo').val(composedEmailString)
+    var formValue = $('#mailNewForm').serialize()
     $.ajax({
-        url: mailbox + "/sendNewMail",
+        url: mailbox + '/sendNewMail',
         data: formValue,
         success: function(result) {
-            $("#newMailEditor").val("")
-            $("#newMailSubject").val("")
-            $("#newMailTo").val("")
+            $('#newMailEditor').val('')
+            $('#newMailSubject').val('')
+            $('#newMailTo').val('')
             closeMailNew()
-            showToast(Severity.Normal, "Email succesfully send")
+            showToast(Severity.Normal, 'Email succesfully send')
         },
         error: function(jqXHR, textStatus, errorThrown) {
-            showToast(Severity.Critical, "Unable to send email: " + errorThrown + " " + textStatus)
+            showToast(Severity.Critical, 'Unable to send email: ' + errorThrown + ' ' + textStatus)
         }
     })
 }
 
 function logout() {
-    window.location.href = "/logout"
+    window.location.href = '/logout'
 }
 
 function settings() {
-    window.location.href = "/settings"
+    window.location.href = '/settings'
 }
 
 function connectNotifier() {
@@ -507,11 +504,11 @@ function connectNotifier() {
         return
     }
 
-    var protocol = "wss://"
-    if(window.location.protocol  !== "https:") {
-        protocol = "ws://"
+    var protocol = 'wss://'
+    if (window.location.protocol  !== 'https:') {
+        protocol = 'ws://'
     }
-    notifierSocket = new WebSocket(protocol + window.location.host + mailbox + "/notifierSubscribe")
+    notifierSocket = new WebSocket(protocol + window.location.host + mailbox + '/notifierSubscribe')
     notifierSocket.onmessage = function (e) {
         for (var i = 0; i < folders.length; i++) {
             folderStat(folders[i])
@@ -529,3 +526,29 @@ $(window).on('beforeunload', function(){
 
 window.onbeforeunload = function() {
 };
+
+
+var selectionList = new Array()
+function selectMail(id, checkbox) {
+    var currentState = $(checkbox).prop('checked')
+    if (currentState == false) {
+        const i = selectionList.indexOf(id);
+        if (i >= 0) {
+            selectionList.splice(i, 1);
+        }
+    } else {
+        selectionList.push(id)
+    }
+
+    if(selectionList.length > 0) {
+        $('#multiActions').css('display', 'flex');
+    } else {
+        $('#multiActions').css('display', 'none');
+    }
+}
+
+function removeSelected(callback) {
+}
+
+function readSelected(callback) {
+}

+ 10 - 10
web/js/notifications.js

@@ -13,28 +13,28 @@ function showToast(severity, text) {
 
     toast.text(text)
 
-    toast.removeClass("normal")
-    toast.removeClass("warning")
-    toast.removeClass("critical")
+    toast.removeClass('normal')
+    toast.removeClass('warning')
+    toast.removeClass('critical')
 
     switch(severity) {
         case Severity.Warning:
-            toast.addClass("warning")
+            toast.addClass('warning')
             break
         case Severity.Critical:
-            toast.addClass("critical")
+            toast.addClass('critical')
             break
         case Severity.Normal:
         default:
-            toast.addClass("normal")
+            toast.addClass('normal')
             break
     }
 
 
-    toast.removeClass("hidden")
-    toast.addClass("visible")
+    toast.removeClass('hidden')
+    toast.addClass('visible')
     setTimeout(function() {
-        toast.removeClass("visible")
-        toast.addClass("hidden")
+        toast.removeClass('visible')
+        toast.addClass('hidden')
     }, 2000)
 }

+ 5 - 0
web/templates/index.html

@@ -16,6 +16,11 @@
         <div id="main">
             <div id="headerBox">
                 <div id="statusLine"></div>
+                <div id="multiActions" class="noselect">
+                    <img class="iconBtn" style="width: 30px; height: 30px; margin: auto auto auto 10px;" onclick="settings(); event.stopPropagation(); return false;" src="/assets/unread.svg"/>
+                    <img class="iconBtn" style="display: none; width: 30px; height: 30px; margin: auto auto auto 10px;" onclick="settings(); event.stopPropagation(); return false;" src="/assets/restore.svg"/>
+                    <img class="iconBtn" style="width: 30px; height: 30px; margin: auto auto auto 10px;" onclick="settings(); event.stopPropagation(); return false;" src="/assets/remove.svg"/>
+                </div>
                 <div id="pager" class="noselect">
                     <img style="width: 20px;" src="/assets/prev.svg" onclick="prevPage()">
                     <div style="width: 60px;display: flex;">

+ 8 - 2
web/templates/maillist.html

@@ -1,14 +1,20 @@
 <!-- <div class="fadeIn" style="position: absolute; top: 5pt; left: 0; right: 0; height: 10pt"></div> -->
 {{range .}}
 <div id="mail{{.Id}}" class="mailHeaderContainer {{if .Read}}read{{else}}unread{{end}}" style="position: relative;" onmouseover="$('#mailControlPanel{{.Id}}').show()" onmouseout="$('#mailControlPanel{{.Id}}').hide()">
-    <div class="mailHeader noselect" onclick="mailOpen('{{.Id}}');" >
+    <div class="mailHeader noselect" onclick="mailOpen('{{.Id}}');">
+        <div style="display: block; margin: 10px 10px;">
+            <label class="cbox" onclick="event.stopPropagation();">
+                <input type="checkbox" onclick="selectMail('{{.Id}}', event.target); event.stopPropagation(); return true;">
+                <span></span>
+            </label>
+        </div>
         <div class="mailFrom elidedText noselect">{{.Mail.Header.From}}</div>
         <div class="mailSubject elidedText noselect">{{.Mail.Header.Subject}}</div>
         <div id="mailDate{{.Id}}" class="mailDate elidedText noselect"><script>localDate('mailDate{{.Id}}', {{.Mail.Header.Date}})</script></div>
     </div>
     <div id="mailControlPanel{{.Id}}" class="mailControlPanel">
         <div style="width: 100%; height: 100%; display: flex; flex-direction: row;">
-            <img id="readListIcon{{.Id}}" class="iconBtn" style="width: 20px; margin-left: 40px; margin-right: 10px;" onclick="toggleRead('{{.Id}}', 'readListIcon'); event.stopPropagation(); console.log(event); return false;" src="/assets/{{if .Read}}read{{else}}unread{{end}}.svg"/>
+            <img id="readListIcon{{.Id}}" class="iconBtn" style="width: 20px; margin-left: 40px; margin-right: 10px;" onclick="toggleRead('{{.Id}}', 'readListIcon'); event.stopPropagation(); return false;" src="/assets/{{if .Read}}read{{else}}unread{{end}}.svg"/>
             <img id="deleteListIcon" class="iconBtn" style="width: 20px; margin-right: 10px;" onclick="removeMail({{.Id}}, closeDetails); event.stopPropagation(); return false;" src="/assets/remove.svg"/>
         </div>
     </div>

Some files were not shown because too many files changed in this diff