diff --git a/client/css/bootstrap.css b/client/css/bootstrap.css index e1d5b223..5f41c698 100644 --- a/client/css/bootstrap.css +++ b/client/css/bootstrap.css @@ -143,6 +143,10 @@ input::-moz-focus-inner { border: 0; padding: 0; } +input::-ms-clear, +input::-ms-reveal { + display: none; +} input { line-height: normal; } diff --git a/client/css/style.css b/client/css/style.css index 6a14aaac..fd26c419 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -1682,6 +1682,54 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ margin-top: 0.2em; } +.password-container { + position: relative; +} + +.password-container input { + padding-right: 37px; +} + +#sign-in .password-container { + width: 100%; +} + +#sign-in .password-container .reveal-password { + top: 31px; + right: 0; +} + +.password-container .reveal-password { + position: absolute; + top: 2px; + right: 15px; +} + +.password-container .reveal-password span { + font: normal normal normal 14px/1 FontAwesome; + font-size: 16px; + color: #cdd3da; + width: 37px; + height: 37px; + display: flex; + justify-content: center; + align-items: center; +} + +.password-container .reveal-password span:hover { + color: #79838c; +} + +.password-container .reveal-password span::before { + content: "\f06e"; /* https://fontawesome.com/icons/eye?style=solid */ + transition: color 0.2s; +} + +.password-container .reveal-password.visible span::before { + content: "\f070"; /* https://fontawesome.com/icons/eye-slash?style=solid */ + color: #ff4136; +} + #help .help-item { display: table-row; font-size: 14px; diff --git a/client/js/socket-events/auth.js b/client/js/socket-events/auth.js index 3a59530e..f89077dd 100644 --- a/client/js/socket-events/auth.js +++ b/client/js/socket-events/auth.js @@ -23,6 +23,8 @@ socket.on("auth", function(data) { login.html(templates.windows.sign_in()); + utils.togglePasswordField("#sign-in .reveal-password"); + login.find("form").on("submit", function() { const form = $(this); diff --git a/client/js/socket-events/configuration.js b/client/js/socket-events/configuration.js index 3465cdb3..a83c0284 100644 --- a/client/js/socket-events/configuration.js +++ b/client/js/socket-events/configuration.js @@ -6,6 +6,7 @@ const templates = require("../../views"); const options = require("../options"); const webpush = require("../webpush"); const connect = $("#connect"); +const utils = require("../utils"); socket.on("configuration", function(data) { if (options.initialized) { @@ -29,6 +30,8 @@ socket.on("configuration", function(data) { pop.play(); }); + utils.togglePasswordField("#change-password .reveal-password"); + options.initialize(); webpush.initialize(); @@ -83,6 +86,8 @@ socket.on("configuration", function(data) { // Store the "previous" value, for next time $(this).data("lastvalue", nick); }); + + utils.togglePasswordField("#connect .reveal-password"); }); if ($(document.body).hasClass("public") && "URLSearchParams" in window) { diff --git a/client/js/socket-events/network.js b/client/js/socket-events/network.js index a6fa04ef..9cf07d67 100644 --- a/client/js/socket-events/network.js +++ b/client/js/socket-events/network.js @@ -5,6 +5,7 @@ const socket = require("../socket"); const render = require("../render"); const templates = require("../../views"); const sidebar = $("#sidebar"); +const utils = require("../utils"); socket.on("network", function(data) { render.renderNetworks(data, true); @@ -41,4 +42,6 @@ socket.on("network:info", function(data) { .text(newName) .click(); }); + + utils.togglePasswordField("#connect .reveal-password"); }); diff --git a/client/js/utils.js b/client/js/utils.js index ceecb05b..aa162d71 100644 --- a/client/js/utils.js +++ b/client/js/utils.js @@ -20,6 +20,7 @@ module.exports = { move, resetHeight, toggleNotificationMarkers, + togglePasswordField, requestIdleCallback, togglePreviewMoreButtonsIfNeeded, }; @@ -97,6 +98,25 @@ function toggleNotificationMarkers(newState) { viewport.toggleClass("notified", newState); } +function togglePasswordField(elem) { + $(elem).on("click", function() { + const $this = $(this); + const input = $this.closest("div").find("input"); + + input.attr("type", input.attr("type") === "password" ? "text" : "password"); + + swapLabel($this); + swapLabel($this.find("span")); + $this.toggleClass("visible"); + }); +} + +// Given a element, swap its aria-label with the content of `data-alt-label` +function swapLabel(element) { + const altText = element.data("alt-label"); + element.data("alt-label", element.attr("aria-label")).attr("aria-label", altText); +} + function confirmExit() { if ($(document.body).hasClass("public")) { window.onbeforeunload = function() { diff --git a/client/views/reveal-password.tpl b/client/views/reveal-password.tpl new file mode 100644 index 00000000..f6cc9812 --- /dev/null +++ b/client/views/reveal-password.tpl @@ -0,0 +1,3 @@ + + + diff --git a/client/views/windows/connect.tpl b/client/views/windows/connect.tpl index bb7bef78..e50ebb5d 100644 --- a/client/views/windows/connect.tpl +++ b/client/views/windows/connect.tpl @@ -78,8 +78,9 @@