152 lines
No EOL
8.4 KiB
JavaScript
152 lines
No EOL
8.4 KiB
JavaScript
(async () => {
|
|
const COHOST_CDN_BASE = "https://staging.cohostcdn.org/";
|
|
const COHOST_STATIC_BASE = "https://cohost.org/static/";
|
|
const CORS_PROXY_BASE = "https://cohost-web-component.meow.garden/";
|
|
const OUTPUT_DIV_ID = "cohost-wc-bookmarklet-output";
|
|
const MEDIA_FLAG_DIV_ID = "cohost-wc-bookmarklet-media-flag";
|
|
const AUDIO_PLAYER_FLAG_DIV_ID = "cohost-wc-audio-player-flag";
|
|
async function maybeConvertToDataUri(imageUrl, shouldConvert) {
|
|
if (!shouldConvert) { return imageUrl; }
|
|
let proxyUrl;
|
|
if (imageUrl.startsWith(COHOST_CDN_BASE)) {
|
|
proxyUrl = imageUrl.replace(COHOST_CDN_BASE, CORS_PROXY_BASE + "cohostcdn-cors-proxy/");
|
|
if (imageUrl.includes("/avatar/")) {
|
|
proxyUrl += "?dpr=2&width=32&height=32&fit=cover&auto=webp";
|
|
}
|
|
} else if (imageUrl.startsWith(COHOST_STATIC_BASE)) {
|
|
proxyUrl = imageUrl.replace(COHOST_STATIC_BASE, CORS_PROXY_BASE + "cohost-static-cors-proxy/");
|
|
} else {
|
|
return imageUrl;
|
|
}
|
|
let response = await fetch(proxyUrl);
|
|
let blob = await response.blob();
|
|
return new Promise((resolve) => {
|
|
let reader = new FileReader();
|
|
reader.onload = () => resolve(reader.result);
|
|
reader.readAsDataURL(blob);
|
|
});
|
|
}
|
|
|
|
async function assembleWebComponent(dataUris) {
|
|
let trpcState = JSON.parse(document.getElementById("trpc-dehydrated-state").innerText);
|
|
let postData = trpcState.queries.find((q) => q.queryKey[0].includes("singlePost")).state.data.post;
|
|
let postElement = document.createElement("cohost-post");
|
|
postElement.setAttribute("avatarSrc", await maybeConvertToDataUri(postData.postingProject.avatarURL, dataUris));
|
|
postElement.setAttribute("avatarShape", postData.postingProject.avatarShape);
|
|
postElement.setAttribute("comments", postData.numComments);
|
|
postElement.setAttribute("displayName", postData.postingProject.displayName);
|
|
postElement.setAttribute("handle", postData.postingProject.handle);
|
|
postElement.setAttribute("permalink", postData.singlePostPageUrl);
|
|
if (postData.publishedAt) {
|
|
postElement.setAttribute("publishedAt", postData.publishedAt);
|
|
}
|
|
if (postData.numSharedComments) {
|
|
postElement.setAttribute("sharedComments", postData.numSharedComments);
|
|
}
|
|
if (postData.headline) {
|
|
postElement.setAttribute("singlePostHeadline", postData.headline);
|
|
}
|
|
postElement.setAttribute("tags", postData.tags.join(","));
|
|
|
|
if (postData.shareTree && postData.shareTree.length) {
|
|
postElement.setAttribute("sharedItems", "");
|
|
let sharedFrom = postData.shareTree[postData.shareTree.length - 1];
|
|
postElement.setAttribute("sharedAvatarSrc", await maybeConvertToDataUri(sharedFrom.postingProject.avatarURL, dataUris));
|
|
postElement.setAttribute("sharedAvatarShape", sharedFrom.postingProject.avatarShape);
|
|
postElement.setAttribute("sharedDisplayName", sharedFrom.postingProject.displayName);
|
|
postElement.setAttribute("sharedHandle", sharedFrom.postingProject.handle);
|
|
|
|
let shareTree = [...postData.shareTree, postData];
|
|
for (sharedPost of shareTree) {
|
|
if (sharedPost.transparentShareOfPostId !== null) {
|
|
continue;
|
|
}
|
|
let sharedElement = document.createElement("cohost-shared-item");
|
|
sharedElement.setAttribute("avatarSrc", await maybeConvertToDataUri(sharedPost.postingProject.avatarURL, dataUris));
|
|
sharedElement.setAttribute("avatarShape", sharedPost.postingProject.avatarShape);
|
|
sharedElement.setAttribute("displayName", sharedPost.postingProject.displayName);
|
|
sharedElement.setAttribute("handle", sharedPost.postingProject.handle);
|
|
if (sharedPost.headline) {
|
|
sharedElement.setAttribute("headline", sharedPost.headline);
|
|
}
|
|
sharedElement.setAttribute("permalink", sharedPost.singlePostPageUrl);
|
|
if (sharedPost.publishedAt) {
|
|
sharedElement.setAttribute("publishedAt", sharedPost.publishedAt);
|
|
}
|
|
sharedElement.setAttribute("tags", sharedPost.tags.join(","));
|
|
let sharedIdElement = document.getElementById(`post-${sharedPost.postId}`);
|
|
if (sharedIdElement) {
|
|
let sharedPostBody = sharedIdElement.parentElement.querySelector("[data-post-body]");
|
|
if (sharedPostBody) {
|
|
sharedElement.innerHTML = sharedPostBody.innerHTML;
|
|
}
|
|
}
|
|
postElement.appendChild(sharedElement);
|
|
}
|
|
} else {
|
|
let postIdElement = document.getElementById(`post-${postData.postId}`);
|
|
if (postIdElement) {
|
|
let postBody = document.getElementById(`post-${postData.postId}`).parentElement.querySelector("[data-post-body]");
|
|
if (postBody) {
|
|
postElement.innerHTML = postBody.innerHTML;
|
|
}
|
|
}
|
|
}
|
|
let mediaFlag = false;
|
|
let audioPlayerFlag = false;
|
|
for (const mediaElement of postElement.querySelectorAll('img, audio')) {
|
|
if (mediaElement.tagName == 'AUDIO') { audioPlayerFlag = true; }
|
|
let src = mediaElement.getAttribute("src");
|
|
if (dataUris && src.startsWith(COHOST_STATIC_BASE)) {
|
|
mediaElement.setAttribute('src', await maybeConvertToDataUri(src, true));
|
|
} else if (src.startsWith("https://cohost.org/") || src.startsWith(COHOST_CDN_BASE)) {
|
|
mediaFlag = true;
|
|
}
|
|
}
|
|
return {
|
|
postElement,
|
|
mediaFlag,
|
|
audioPlayerFlag,
|
|
};
|
|
}
|
|
async function renderOutput(webComponent, dataUris) {
|
|
let template = document.createElement('template');
|
|
template.innerHTML = `<div id="${OUTPUT_DIV_ID}" class="fixed bottom-0 p-2 flex flex-col items-center gap-2 font-sm bg-background co-themed-box border rounded-lg">
|
|
<textarea rows="5" cols="35" style="font-size: inherit; line-height: 1.4"></textarea>
|
|
<div id="${MEDIA_FLAG_DIV_ID}" class="co-info-box co-info text-sm mx-auto w-full rounded-lg p-3">
|
|
Post <b>still</b> contains media hosted on cohost.<br />Consider rehosting any images or audio files on your website.
|
|
</div>
|
|
<div id="${AUDIO_PLAYER_FLAG_DIV_ID}" class="co-info-box co-info text-sm mx-auto w-full rounded-lg p-3">
|
|
cohost's audio player doesn't function correctly (yet).
|
|
</div>
|
|
<div class="flex flex-row gap-1">
|
|
<button id="cohost-wc-data-uris-button" class="rounded-lg bg-cherry py-2 px-4 text-sm font-bold text-notWhite hover:bg-cherry-600 active:bg-cherry-700">Convert avatars and custom emoji to data URIs</button>
|
|
<button id="cohost-wc-dismiss-button" class="rounded-lg bg-cherry py-2 px-4 text-sm font-bold text-notWhite hover:bg-cherry-600 active:bg-cherry-700">dismiss</button>
|
|
</div>
|
|
</div>`;
|
|
template.content.querySelector('textarea').value = webComponent.postElement.outerHTML;
|
|
if (!webComponent.mediaFlag) {
|
|
template.content.querySelector(`#${MEDIA_FLAG_DIV_ID}`).remove();
|
|
} else if (!dataUris) {
|
|
template.content.querySelector(`#${MEDIA_FLAG_DIV_ID} b`).remove();
|
|
}
|
|
if (!webComponent.audioPlayerFlag) {
|
|
template.content.querySelector(`#${AUDIO_PLAYER_FLAG_DIV_ID}`).remove();
|
|
}
|
|
template.content.querySelector("#cohost-wc-data-uris-button").onclick = async () => {
|
|
let webComponent = await assembleWebComponent(true);
|
|
renderOutput(webComponent, true);
|
|
}
|
|
template.content.querySelector("#cohost-wc-dismiss-button").onclick = function() {
|
|
document.querySelector(`#${OUTPUT_DIV_ID}`).remove();
|
|
}
|
|
let existingOutput = document.querySelector(`#${OUTPUT_DIV_ID}`);
|
|
if (existingOutput) {
|
|
existingOutput.replaceWith(template.content);
|
|
} else {
|
|
document.body.append(template.content);
|
|
}
|
|
}
|
|
let webComponent = await assembleWebComponent(false);
|
|
renderOutput(webComponent, false);
|
|
})(); |