Skip to content

Instantly share code, notes, and snippets.

@porglezomp
Last active November 15, 2021 02:46
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 porglezomp/34be1368089c721291d309d2d81fe560 to your computer and use it in GitHub Desktop.
Save porglezomp/34be1368089c721291d309d2d81fe560 to your computer and use it in GitHub Desktop.
Show your pinned lists in your twitter top bar.
// ==UserScript==
// @name Twitter Pinned Lists
// @description Show your pinned lists in your twitter top bar.
// @version 1.1
// @grant none
// @include https://twitter.com/*
// @include https://mobile.twitter.com/*
// ==/UserScript==
// USAGE: Visit your lists page for a few seconds, and this will learn your pinned lists.
// Then it should be able to insert the pinned lists into the top bar.
const PRIMARY_COLUMN = "main [data-testid='primaryColumn']";
const LOCALSTORAGE_KEY = "pinnedLists#greaseMonkey@porglezomp";
// Twitter is a rude SPA that doesn't load the header promptly...
// This will just keep trying to find an element until you see it!
function eventuallyGetElement(selector) {
return new Promise((resolve, reject) => {
function find() {
console.log(`Trying to find ${selector}`);
let element = document.querySelector(selector);
if (element) return resolve(element);
window.setTimeout(find, 1000);
}
find();
});
}
// We have the pinned lists saved in local storage,
// so we don't rely on the user going and looking at their lists page each session.
let pinnedLists = JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY) || '[]');
// Twitter broke the storage/API I was using to find the list names...
// So we'll discover them when you visit your lists page and see the
// pinned lists there. Save them to local storage when you find them.
setInterval(() => {
try {
// If we're not on a "lists" page, then we don't need to try to discover lists
if (!window.location.pathname.endsWith("/lists")) {
return;
}
const pinnedListHeader =
Array.from(document.querySelectorAll('h2'))
.find(x => x.textContent.startsWith("Pinned Lists"));
// If there is no "Pinned Lists" header then this might be another user's lists page
if (!pinnedListHeader) {
return;
}
// The actual pinned lists are a sibling of an ancestor...
const pinnedListsContainer = pinnedListHeader
.parentElement.parentElement.parentElement.nextSibling;
const pinnedListElements = pinnedListsContainer.querySelectorAll('li');
// Get the info and save it into localStorage for later loads.
let pinned = [];
for (const list of pinnedListElements) {
const a = list.querySelector('a');
pinned.push({ name: a.textContent, href: a.href });
}
// Only overwrite for non-empty lists,
// because sometimes we get an empty list due to a partially loaded lists page.
console.log("Saving", pinned);
if (pinned.length) {
pinnedLists = pinned;
localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(pinnedLists));
}
} catch (err) {
console.log(err);
}
}, 2000);
// Insert the list links into the page header.
function injectLinks(header, headerLine, lists) {
if (headerLine.childElementCount != 1) return;
headerLine.style["flex-direction"] = "row";
headerLine.style["gap"] = "15px";
headerLine.style["align-items"] = "baseline";
let i = 1;
for (const list of lists) {
let element = document.createElement("h2");
element.classList = header.classList;
let link = document.createElement("a");
element.appendChild(link);
link.href = list.href;
link.text = list.name;
link.style["color"] = getComputedStyle(header).color;
link.style["font-size"] = "0.9em";
headerLine.appendChild(element);
i += 1;
}
}
// Every few seconds, try to inject the links into the header.
// We have to do this repeatedly, because they go away when users navigate pages.
async function findAndInject() {
try {
let primaryColumn = await eventuallyGetElement(PRIMARY_COLUMN);
let titleContainer = primaryColumn.firstElementChild.firstElementChild;
let header = titleContainer.querySelector('h2');
let headerLine = header.parentElement;
injectLinks(header, headerLine, pinnedLists);
} catch (err) {
console.log(err);
}
setTimeout(findAndInject, 1000);
}
findAndInject();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment