Skip to content

Instantly share code, notes, and snippets.

@dceddia
Last active February 14, 2022 19:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dceddia/86b8817ec6818342b888a117ea12bd6b to your computer and use it in GitHub Desktop.
Save dceddia/86b8817ec6818342b888a117ea12bd6b to your computer and use it in GitHub Desktop.
Hide unsubscribers in ConvertKit (or replace them with a "% Stayed" instead)
// ==UserScript==
// @name CK - Hide or replace unsubs
// @namespace Violentmonkey Scripts
// @match https://app.convertkit.com/*
// @grant none
// @version 1.0
// @author -
// @description Hides the Unsubscribers number on broadcasts, or replaces it with the percentage of people who stayed.
// ==/UserScript==
// HOW TO USE THIS:
//
// - Install the Violentmonkey extension for your browser
// - Click the extension's icon, click the + button to create a new script
// - Select all text, delete it, and replace it with the contents of this file
//
// HOW TO MODIFY:
//
// It's got 2 modes
// - hide unsubs
// - replace unsubs with "Stayed" (the percentage of people who DIDN'T unsubscribe)
//
// To change the mode, change 'hide' to 'replace' here:
const MODE = 'hide';
///////////////////////////////////
function waitUntilReady() {
if(getNavbar()) {
modifyPage();
} else {
setTimeout(waitUntilReady, 100);
}
}
function modifyPage() {
const isBroadcastsPage = [...document.querySelectorAll('h2')].some(tag => /Broadcasts/.test(tag.innerText));
if(isBroadcastsPage) {
modifyBroadcasts();
}
}
function modifyBroadcasts() {
if(MODE === 'hide') {
repeatUntilDone(hideUnsubs);
} else {
repeatUntilDone(replaceUnsubs);
}
}
function repeatUntilDone(func) {
const stats = getStats();
// If no stats are on the page yet, retry until they are
if(!stats.some(bs => /Recipients?/.test(bs.innerText))) {
setTimeout(() => repeatUntilDone(func), 100);
return;
}
// Repeat until 'Unsubscribers' is gone. (Some rows take longer to load than others)
const loadedRows = stats.filter(bs => /Recipients/.test(bs.innerText));
if(loadedRows.some(bs => /Unsubscribers?/.test(bs.innerText))) {
func(stats);
setTimeout(() => repeatUntilDone(func), 100);
}
}
function hideUnsubs(stats) {
stats.forEach(row => {
[...row.querySelectorAll('a')]
.filter(a => /Unsubscribers/.test(a.innerText))
.forEach(link => {
if(!link) return;
const dot = link.previousElementSibling;
if(dot) dot.remove();
link.remove();
});
});
}
function replaceUnsubs(stats) {
stats.forEach(row => {
const links = [...row.querySelectorAll('a')];
const recipLink = links.filter(a => /Recipients?/.test(a.innerText))[0];
const unsubLink = links.filter(a => /Unsubscribers?/.test(a.innerText))[0];
if(!recipLink || !unsubLink) return;
const dot = unsubLink.previousElementSibling;
const recipients = parseFloat(recipLink.innerText.replace(/,/g, ''));
const unsubs = parseFloat(unsubLink.innerText.replace(/,/g, ''));
const stayed = 100.0 * (recipients - unsubs) / recipients;
// handle junk
if(isNaN(stayed)) {
unsubLink.remove();
dot.remove();
return
}
unsubLink.innerText = stayed.toFixed(1) + '% Stayed';
unsubLink.removeAttribute('href');
});
}
function getNavbar() {
return document.querySelector('nav');
}
function getStats() {
return [...document.querySelectorAll('.broadcast_stats')];
}
waitUntilReady();
@Maximization
Copy link

Maximization commented Nov 19, 2021

@dceddia If you change the Regexes to /Unsubscribers?/ it'll also work when there is 1 unsub :)

@dceddia
Copy link
Author

dceddia commented Nov 19, 2021

@Maximization Good call! Fixed 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment