Last active
September 12, 2024 03:47
-
-
Save donaldguy/8b56909e704f2977914ac61ec586c735 to your computer and use it in GitHub Desktop.
app.hey.com: add unread counts and auto-advance to the "Imbox" - very much a work in progress
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Add counts to Hey.com Imbox (and contents) | |
// @run-at document-end | |
// @match https://app.hey.com/* | |
// @grant GM_getValue | |
// @grant GM_setValue | |
const BASE_URL_PATTERN = 'https://app\\.hey\\.com' | |
let UnreadCount = -1; | |
let SeenCount = -999; | |
let CachedUnreadURLs = new Set() | |
let ShouldAutoAdvance = false | |
function countsText() { | |
if (UnreadCount < 0) { | |
return "" | |
} | |
if (SeenCount < 0) { | |
return ` (${UnreadCount})` | |
} | |
return ` (${UnreadCount} / ${UnreadCount + SeenCount})` | |
} | |
class Page { | |
static url_pattern = new RegExp(`${BASE_URL_PATTERN}/.+`); | |
loaded() { return; } | |
} | |
const Imbox = new (class Imbox extends Page { | |
url_pattern = new RegExp(`^${BASE_URL_PATTERN}/$|^${BASE_URL_PATTERN}/imbox`); | |
get(_url) { return this } | |
loaded() { | |
this.postings = document.getElementById("postings") | |
this.selectUnread() | |
if (UnreadCount < 0) { | |
this.insertCountButton("Count Unread", this.scrollToLoadUnread.bind(this)) | |
} else if (SeenCount < 0) { | |
this.insertCountButton("Count Total", this.scrollToLoadAll.bind(this)) | |
} | |
this.processUnread() | |
if (ShouldAutoAdvance) { | |
this.auto_advance() | |
} else { | |
const titleWithCounts = `Imbox${countsText()}` | |
document.getElementsByTagName('h1')[0].innerText = titleWithCounts | |
document.title = titleWithCounts | |
} | |
} | |
auto_advance() { | |
ShouldAutoAdvance = false; | |
this.postings.querySelector("a.permalink").click() | |
} | |
selectUnread() { | |
this.unreadEmailArticles = this.postings.querySelectorAll('article[data-new-for-you="true"]'); | |
} | |
insertCountButton(text, handler) { | |
const sheetActions = document.getElementsByClassName('sheet-actions')[0]; | |
const firstButton = sheetActions.querySelector('a.btn'); | |
this.countButton = document.createElement('a') | |
this.countButton.href = "#" | |
this.countButton.classList.add('btn') | |
this.countButton.classList.add('btn--secondary') | |
this.countButton.innerText = text | |
this.countButton.addEventListener('click', (e) => { | |
e.preventDefault(); | |
handler() | |
}); | |
sheetActions.insertBefore(this.countButton, firstButton) | |
} | |
scrollToLoadUnread() { | |
if (!this.scrollPoller) { | |
this.scrollPoller = setInterval(this.scrollToLoadUnread.bind(this), 300) | |
return | |
} | |
const readEmailLoaded = this.postings.querySelector('article[data-seen="true"]') | |
if (!readEmailLoaded) { | |
const oldestNewEmail = this.unreadEmailArticles[this.unreadEmailArticles.length - 1] | |
oldestNewEmail.scrollIntoView(true) | |
this.selectUnread() | |
return | |
} else { | |
readEmailLoaded.scrollIntoView(true) | |
this.selectUnread() | |
} | |
// if we made it to here, we have an old email in view and fetching | |
// _should_ be done | |
clearInterval(this.scrollPoller) | |
this.scrollPoller = false | |
UnreadCount = this.unreadEmailArticles.length | |
CachedUnreadURLs = new Set() | |
this.countButton.remove() | |
window.scrollTo(0, 0) | |
this.loaded() | |
} | |
scrollToLoadAll() { | |
// XXX: only works if cover is not active; state is in localStorage.peeking | |
// but how to mutate best? | |
if (!this.scrollPoller) { | |
this.scrollPoller = setInterval(this.scrollToLoadAll.bind(this), 800) | |
return | |
} | |
if (UnreadCount > 0) { | |
this.unreadEmailArticles[this.unreadEmailArticles.length - 1].scrollIntoView(true) | |
} | |
const readEmailLoaded = this.postings.querySelectorAll('article[data-seen="true"]') | |
if (!readEmailLoaded) { | |
return | |
} | |
const paginationLinkExists = this.postings.querySelector('.pagination-link') | |
while (paginationLinkExists) { | |
const oldestEmail = readEmailLoaded[readEmailLoaded.length - 1] | |
oldestEmail.scrollIntoView(true) | |
return | |
} | |
// if we made it to here, we have an old email in view and fetching | |
// _should_ be done | |
clearInterval(this.scrollPoller) | |
this.scrollPoller = false | |
SeenCount = readEmailLoaded.length | |
this.countButton.remove() | |
window.scrollTo(0, 0) | |
this.loaded() | |
} | |
processUnread() { | |
for (const article of this.unreadEmailArticles) { | |
CachedUnreadURLs.add(article.querySelector('a.permalink').href) | |
} | |
} | |
})() | |
class Thread extends Page { | |
static url_pattern = new RegExp(`^${BASE_URL_PATTERN}/topics/[^/]+$`); | |
constructor(url) { | |
super() | |
this.url = url | |
this.was_unread = CachedUnreadURLs && CachedUnreadURLs.has(this.url) | |
} | |
static get(url) { return new Thread(url); } | |
loaded() { | |
document.title = `${document.title}${countsText()}` | |
} | |
} | |
// --------- | |
class GMHeyNavigation { | |
constructor(from_page, to_page, why) { | |
this.from_page = from_page | |
this.to_page = to_page | |
this.why = why | |
console.log(this); | |
if (!from_page) { | |
return; | |
} | |
if (to_page.constructor === Thread && to_page.was_unread) { | |
UnreadCount -= 1 | |
SeenCount += 1 | |
CachedUnreadURLs.delete(from_page.url) | |
} | |
if (from_page.constructor === Thread && why == 'unseen') { | |
UnreadCount += 1 | |
SeenCount -= 1 | |
CachedUnreadURLs.add(from_page.url) | |
} | |
if (from_page.constructor === Thread && why == 'status/trashed') { | |
SeenCount -= 1 | |
} | |
//unless its being moved _to_ the Imbox | |
if (from_page.constructor === Thread && why.startsWith('moves')) { | |
SeenCount -= 1 | |
} | |
if (from_page.constructor === Thread && to_page === Imbox && why != 'back' && why != 'unseen') { | |
ShouldAutoAdvance = true | |
} | |
} | |
} | |
// -------- | |
const detectPage = (url) => { | |
for (page of [Imbox, Thread]) { | |
if (page.url_pattern.test(url)) { | |
return page.get(url) | |
} | |
} | |
} | |
let CurrentPage = detectPage(window.location.href); | |
(function() { | |
new GMHeyNavigation( | |
undefined, | |
CurrentPage, | |
'load' | |
); | |
document.addEventListener('turbo:visit', function(event) { | |
var from = CurrentPage | |
var to = detectPage(event.detail.url) | |
var why = 'visit'; | |
if (from.exit_reason) { | |
why = from.exit_reason | |
} else if (to === Imbox && event.detail.action == 'restore') { | |
why = 'back' | |
} | |
new GMHeyNavigation(from, to, why); | |
CurrentPage = to; | |
return true; | |
}); | |
document.addEventListener('turbo:load', function() { | |
CurrentPage.loaded(); | |
}); | |
document.addEventListener('turbo:submit-end', function(event) { | |
if (!event.detail.success) { | |
return true; | |
} | |
if (CurrentPage.constructor === Thread && event.target.action.startsWith(CurrentPage.url)) { | |
CurrentPage.exit_reason = event.target.action.substr(CurrentPage.url.length + 1) | |
} | |
return true; | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Posted on reddit (and elaborated known issues, inter alia) here: https://www.reddit.com/r/HeyEmail/comments/1enms2q/sharing_my_userscript_js_arc_boost_imbox/