Skip to content

Instantly share code, notes, and snippets.

@kevinleedrum
Last active June 15, 2023 14:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinleedrum/d7e261c9d9b0b3281dcc75c16d69f143 to your computer and use it in GitHub Desktop.
Save kevinleedrum/d7e261c9d9b0b3281dcc75c16d69f143 to your computer and use it in GitHub Desktop.
Proton Mail Unread Favicon
// ==UserScript==
// @name Proton Mail Unread Favicon
// @namespace https://gist.github.com/kevinleedrum/d7e261c9d9b0b3281dcc75c16d69f143
// @version 0.1
// @description Updates the Proton Mail favicon to include a single-digit unread count
// @author Kevin Lee Drum
// @match https://mail.proton.me/u/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=proton.me
// @grant none
// ==/UserScript==
const INBOX_SELECTOR = '[data-testid="navigation-link:inbox"]';
const UNREAD_SELECTOR = `${INBOX_SELECTOR} [data-unread-count]`;
const BADGE_BG = "#fff";
const BADGE_FG = "#000";
const INBOX_TIMEOUT = 10; // 5 seconds
let inboxChecks = 0;
let defaultFavicon;
(function () {
let waitForInbox = setInterval(() => {
const inbox = document.querySelector(INBOX_SELECTOR);
if (inboxChecks > INBOX_TIMEOUT) {
console.error("Proton Mail Unread Favicon: Timed out waiting for inbox");
clearInterval(waitForInbox);
return;
}
inboxChecks++;
if (!inbox) return;
clearInterval(waitForInbox);
getDefaultFavicon();
updateFavicon();
watchForInboxChange();
}, 500);
})();
function getDefaultFavicon() {
const svgFavicon = document.querySelector(
'link[rel="icon"][type="image/svg+xml"]'
);
if (svgFavicon) svgFavicon.remove();
const favicon = document.querySelector('link[rel="icon"]');
defaultFavicon = favicon.href;
}
function watchForInboxChange() {
const inbox = document.querySelector(INBOX_SELECTOR);
if (!inbox) return;
const observer = new MutationObserver(updateFavicon);
observer.observe(inbox, { subtree: true, attributes: true, childList: true });
}
function updateFavicon() {
const favicon = document.querySelector('link[rel="icon"]');
const unreadCounter = document.querySelector(UNREAD_SELECTOR);
let count = 0;
if (unreadCounter) count = +unreadCounter.dataset.unreadCount;
if (!count) {
favicon.href = defaultFavicon;
return;
}
if (count > 9) count = "*";
drawFavicon(count, favicon);
}
function drawFavicon(count, favicon) {
const canvas = document.createElement("canvas");
canvas.width = 16;
canvas.height = 16;
const ctx = canvas.getContext("2d");
const img = document.createElement("img");
img.src = defaultFavicon;
img.onload = () => {
// Draw default favicon
ctx.drawImage(img, 0, 0, 16, 16);
// Add circle
ctx.fillStyle = BADGE_BG;
ctx.beginPath();
ctx.arc(12, 4, 5, 0, 2 * Math.PI);
ctx.fill();
// Add count
ctx.fillStyle = BADGE_FG;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "10px sans-serif";
ctx.fillText(count, 12, 5);
// Update favicon href
favicon.href = canvas.toDataURL("image/png");
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment