Instantly share code, notes, and snippets.
Last active
November 23, 2023 01:50
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save cefqrn/069d702ba83826199c90b9ec748f777c to your computer and use it in GitHub Desktop.
see https://cohost.org/cefqrn/post/1430390-header-buttons-users for details
This file contains 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 Header Buttons | |
// @version 0.36 | |
// @description Adds like and share buttons to the headers of shared posts | |
// @author cefqrn | |
// @match https://cohost.org/* | |
// ==/UserScript== | |
const SHARE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" class="h-6 w-6 co-action-button"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"></path></svg>` | |
const HEART_PATH_ACTIVE = `<path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 01-.383-.218 25.18 25.18 0 01-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0112 5.052 5.5 5.5 0 0116.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 01-4.244 3.17 15.247 15.247 0 01-.383.219l-.022.012-.007.004-.003.001a.752.752 0 01-.704 0l-.003-.001z"></path>` | |
const HEART_PATH_INACTIVE = `<path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"></path>` | |
const SVG_NS = "http://www.w3.org/2000/svg"; | |
(async function() { | |
"use strict"; | |
// get the user id and handle from the user's profile picture | |
const avatar = await new Promise((resolve) => { | |
const intervalID = setInterval(() => { | |
const result = document.querySelector("button[id^=headlessui-listbox-button] div img"); | |
if (result) { | |
clearInterval(intervalID); | |
resolve(result); | |
} | |
}, 100); | |
}); | |
// the user id is the first part of the avatar's filename | |
const userID = parseInt(avatar.src.match(/avatar\/(\d+)/)[1]); | |
// the handle is used as alt text for profile pictures | |
const handle = avatar.alt; | |
// posts on the current page | |
const posts = {}; | |
function initializeHeader(header) { | |
const postURL = header.querySelector("time a").href; | |
if (postURL === "https://cohost.org/") { | |
// post is deleted | |
return; | |
} | |
const postID = parseInt(postURL.match(/post\/(\d+)/)[1]); | |
const iconContainer = document.createElement("div"); | |
iconContainer.classList.add("flex", "items-center", "justify-end", "gap-3", "ml-auto"); | |
header.appendChild(iconContainer); | |
const shareLink = document.createElement("a"); | |
shareLink.href = `https://cohost.org/${handle}/post/compose?shareOf=${postID}`; | |
shareLink.title = `share this post as ${handle}`; | |
shareLink.innerHTML = SHARE_ICON; | |
iconContainer.appendChild(shareLink); | |
const likeButton = document.createElement("button"); | |
likeButton.classList.add("w-6", "h-6", "pointer", "relative"); | |
likeButton.title = `like this post as ${handle}` | |
iconContainer.appendChild(likeButton); | |
const activeLikeIcon = document.createElementNS(SVG_NS, "svg"); | |
activeLikeIcon.setAttributeNS(null, "fill", "currentColor"); | |
activeLikeIcon.setAttributeNS(null, "viewBox", "0 0 24 24"); | |
activeLikeIcon.setAttributeNS(null, "aria-hidden", "true"); | |
activeLikeIcon.classList.add("w-6", "h-6", "pointer", "absolute", "top-0", "left-0", "text-cherry", "invisible"); | |
activeLikeIcon.innerHTML = HEART_PATH_ACTIVE; | |
likeButton.appendChild(activeLikeIcon); | |
const inactiveLikeIcon = document.createElementNS(SVG_NS, "svg"); | |
inactiveLikeIcon.setAttributeNS(null, "fill", "none"); | |
inactiveLikeIcon.setAttributeNS(null, "viewBox", "0 0 24 24"); | |
inactiveLikeIcon.setAttributeNS(null, "stroke-width", "1.5"); | |
inactiveLikeIcon.setAttributeNS(null, "stroke", "currentColor"); | |
inactiveLikeIcon.setAttributeNS(null, "aria-hidden", "true"); | |
inactiveLikeIcon.classList.add("w-6", "h-6", "pointer", "absolute", "top-0", "left-0", "co-action-button", "visible"); | |
inactiveLikeIcon.innerHTML = HEART_PATH_INACTIVE; | |
likeButton.appendChild(inactiveLikeIcon); | |
function updateLikeIcon() { | |
const liked = posts[postID].isLiked; | |
likeButton.title = `${liked ? "unlike" : "like"} this post as ${handle}` | |
if (liked) { | |
activeLikeIcon.classList.replace("invisible", "visible"); | |
inactiveLikeIcon.classList.replace("visible", "invisible"); | |
} else { | |
inactiveLikeIcon.classList.replace("invisible", "visible"); | |
activeLikeIcon.classList.replace("visible", "invisible"); | |
} | |
} | |
if (postID in posts) { | |
posts[postID].likeUpdateFunctions.push(updateLikeIcon); | |
updateLikeIcon(); | |
} else { | |
posts[postID] = { isLiked: false, likeUpdateFunctions: [updateLikeIcon] }; | |
} | |
likeButton.onclick = (event) => { | |
const post = posts[postID]; | |
const liked = post.isLiked; | |
fetch( | |
`https://cohost.org/api/v1/trpc/relationships.${liked ? "unlike" : "like"}?batch=1`, | |
{ | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
}, | |
method: 'POST', | |
body: JSON.stringify({"0": {fromProjectId: userID, toPostId: postID}}) | |
} | |
).then(res => { | |
if (res.ok) { | |
post.isLiked = !liked; | |
post.likeUpdateFunctions.forEach(func => func()); | |
} | |
}); | |
} | |
header.dataset.headerLikeButtonInitialized = true; | |
} | |
function updatePosts() { | |
Array.from(document.querySelectorAll("[id^=post] + div")) | |
.filter(div => div.className !== "" && !div.dataset.headerLikeButtonInitialized) | |
.map(initializeHeader); | |
} | |
// initialize posts every time the page updates | |
(new MutationObserver(updatePosts)).observe(document, {subtree: true, childList: true}); | |
// initialize the posts that were loaded before the script started | |
updatePosts(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment