From 6988fbcb5bf73b5848df338ade7dca9456c3b47e Mon Sep 17 00:00:00 2001 From: CatAClock Date: Tue, 6 May 2025 15:59:11 -0700 Subject: [PATCH 01/10] Bsky, Mastodon private now show up when you have cookie --- JS/BlueskyAPI.js | 32 ++++++++++++++++++++ JS/MastodonAPI.js | 74 +++++++++++++++++++++++++++-------------------- JS/index.js | 45 +++++++++++++++++++--------- JS/post.js | 2 -- index.html | 2 ++ 5 files changed, 108 insertions(+), 47 deletions(-) diff --git a/JS/BlueskyAPI.js b/JS/BlueskyAPI.js index c5ce038..7d1e951 100644 --- a/JS/BlueskyAPI.js +++ b/JS/BlueskyAPI.js @@ -1,6 +1,34 @@ import * as Variables from "./Variables.js"; +export async function GetTimeline() { + if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { + console.log("No access token!"); + return ""; + } + let PDS = localStorage.getItem(Variables.BlueskyPDS); + let DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getTimeline"); + let request = fetch(PDS + "/xrpc/app.bsky.feed.getTimeline", {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + let body = await request.then((response) => response.json()); + let status = await request.then((response) => response.status); + let header = await request.then((response) => response.headers.get("dpop-nonce")); + if (status == 401) { + if (body.message.includes("DPoP nonce mismatch")) { + await localStorage.setItem(Variables.BlueskyNonce, header); + } + if (body.message.includes("claim timestamp check failed")) { + await RefreshTokens(); + } + body = await GetTimeline(); + } + return body; +} + export async function CreatePost(DID, Text) { + if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { + console.log("No access token!"); + return ""; + } + let PDS = localStorage.getItem(Variables.BlueskyPDS); let Json = { "$type": "app.bsky.feed.post", @@ -30,6 +58,10 @@ export async function CreatePost(DID, Text) { } export async function SetThreadGate(DID, Post, VisibilitySettings) { + if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { + console.log("No access token!"); + return ""; + } let PDS = localStorage.getItem(Variables.BlueskyPDS); let Json = { "$type": "app.bsky.feed.threadgate", diff --git a/JS/MastodonAPI.js b/JS/MastodonAPI.js index d5701fa..aefa1b5 100644 --- a/JS/MastodonAPI.js +++ b/JS/MastodonAPI.js @@ -12,67 +12,77 @@ export async function GetPublicTimeline(Local = false, Remote = false, Website) } if (Local == true) { - Timeline = await fetch(Website + "/api/v1/timelines/public?limit=12&local=true") + Timeline = await fetch(Website + "/api/v1/timelines/public?local=true") .then((response) => response.json()); } else if (Remote == true) { - Timeline = await fetch(Website + "/api/v1/timelines/public?limit=12&remote=true") + Timeline = await fetch(Website + "/api/v1/timelines/public?remote=true") .then((response) => response.json()); } else { - Timeline = await fetch(Website + "/api/v1/timelines/public?limit=12") + Timeline = await fetch(Website + "/api/v1/timelines/public") .then((response) => response.json()); } return Timeline; } +export async function GetTimeline() { + if (localStorage.getItem(Variables.MastodonAccessToken) == null) { + console.log("No access token!"); + return ""; + } + let Website = localStorage.getItem(Variables.MastodonWebsite); + // Get the varaibles that are stored in localStorage. + return await fetch(Website + "/api/v1/timelines/home", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + .then((response) => response.json()); +} + // Gets the favorites of a user that you have access to. export async function GetFavorites() { - let Favorites; - // Check for a token. - if (localStorage.getItem(Variables.MastodonAccessToken) != null) { - let Website = Variables.MastodonWebsite; - // Get the varaibles that are stored in localStorage. - Favorites = await fetch(Website + "/api/v1/favourites", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) - .then((response) => response.json()); + if (localStorage.getItem(Variables.MastodonAccessToken) == null) { + console.log("No access token!"); + return ""; } - return Favorites; + let Website = localStorage.getItem(Variables.MastodonWebsite); + // Get the varaibles that are stored in localStorage. + return await fetch(Website + "/api/v1/favourites", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + .then((response) => response.json()); } // Gets the bookmarks of a user that you have access to. export async function GetBookmarks() { - let Bookmarks; - // Check for a token. - if (localStorage.getItem(Variables.MastodonAccessToken) != null) { - let Website = localStorage.getItem(Variables.MastodonWebsite); - // Get the varaibles that are stored in Variables. - Bookmarks = await fetch(Website + "/api/v1/bookmarks", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) - .then((response) => response.json()); + if (localStorage.getItem(Variables.MastodonAccessToken) == null) { + console.log("No access token!"); + return ""; } - return Bookmarks; + let Website = localStorage.getItem(Variables.MastodonWebsite); + // Get the varaibles that are stored in Variables. + return await fetch(Website + "/api/v1/bookmarks", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + .then((response) => response.json()); } // Gets the notifications of a user that you have access to. export async function GetNotifications() { - let Notifications; - // Check for a token. - if (localStorage.getItem(Variables.MastodonAccessToken) != null) { - let Website = localStorage.getItem(Variables.MastodonWebsite); - // Get the varaibles that are stored in Variables and then input it. - Notifications = await fetch(Website + "/api/v1/notifications", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) - .then((response) => response.json()); + if (localStorage.getItem(Variables.MastodonAccessToken) == null) { + console.log("No access token!"); + return ""; } - return Notifications; + let Website = localStorage.getItem(Variables.MastodonWebsite); + // Get the varaibles that are stored in Variables and then input it. + return await fetch(Website + "/api/v1/notifications", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + .then((response) => response.json()); } // Make a status export async function CreateStatus(Text, Visibility = "public") { + if (localStorage.getItem(Variables.MastodonAccessToken) == null) { + console.log("No access token!"); + return ""; + } // Stolen from StackOverflow Text = Text.replace(/(?:\r|\n|\r\n)/g, '
'); // Check for a token - if (localStorage.getItem(Variables.MastodonAccessToken) != null) { - let Website = localStorage.getItem(Variables.MastodonWebsite); - return await fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) - .then((response) => response.json()); - } + let Website = localStorage.getItem(Variables.MastodonWebsite); + return await fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + .then((response) => response.json()); } // The first step to using the app. diff --git a/JS/index.js b/JS/index.js index 79c8cfb..3255833 100644 --- a/JS/index.js +++ b/JS/index.js @@ -102,30 +102,49 @@ ArrowsButton[0].onclick = (event) => { PosterContainerUpdate(); } -// MastodonAPI integration +// Call this to update the things :) async function PosterContainerUpdate() { + let Lim = 6; // Variables for the public timelines let LocalVar = localStorage.getItem("Local"); var LocalTrue = (LocalVar === "true"); let RemoteVar = localStorage.getItem("Remote"); var RemoteTrue = (RemoteVar === "true"); let Website = localStorage.getItem(Variables.MastodonWebsite); - + // Get the websites backwards. To see if I need more posts from Mastodon + let BlueskyTimeline; + if (localStorage.getItem(Variables.BlueskyPDS) == null) { + console.log("No Bluesky instance. multiplying mastodon posts by 2..."); + Lim = Lim * 2; + } else { + BlueskyTimeline = await BlueskyAPI.GetTimeline(); + } + let MastodonTimeline; if (localStorage.getItem(Variables.MastodonWebsite) == null) { - Website = "https://wetdry.world"; - } - - let Timeline = await MastodonAPI.GetPublicTimeline(LocalTrue, RemoteTrue, Website); - let Content = []; - let Users = []; - for (let i in Timeline) { - Content[i] = Timeline[i].content; - Users[i] = Timeline[i].account.username; + // The default website is Wetdry :3 + MastodonTimeline = await MastodonAPI.GetPublicTimeline(LocalTrue, RemoteTrue, "https://wetdry.world"); + } else { + MastodonTimeline = await MastodonAPI.GetTimeline(); } + let counter = 0; + let countergroup = 0; let CenterContainer = document.getElementsByClassName("PostContainer")[2].children; for (let i in CenterContainer) { - CenterContainer[i].getElementsByClassName("PostContent")[0].innerHTML = Content[i]; - CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = Users[i]; + switch(countergroup) { + case 0: + CenterContainer[i].getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; + CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + break; + case 1: + CenterContainer[i].getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; + CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + break; + } + counter = counter + 1; + if (counter >= Lim) { + counter = 0; + countergroup = countergroup + 1; + } } } diff --git a/JS/post.js b/JS/post.js index ac0de1d..d729be6 100644 --- a/JS/post.js +++ b/JS/post.js @@ -57,9 +57,7 @@ async function Post() { break; } let Post = await BlueskyAPI.CreatePost(localStorage.getItem(Variables.BlueskyDID), Text); - console.log(Post); let ThreadGate = await BlueskyAPI.SetThreadGate(localStorage.getItem(Variables.BlueskyDID), Post.uri, TempVisible); - console.log(ThreadGate); } InputArea.value = ""; } diff --git a/index.html b/index.html index 0489f4c..e80d956 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,8 @@ This is the only way I knew how to make a "non-module" inherit a module. I think this is why people use typescript or other frameworks. --> + + -- 2.45.3 From f41955432b6dc2f15048ae0ab6fad9e1fe607fb9 Mon Sep 17 00:00:00 2001 From: CatAClock Date: Tue, 6 May 2025 16:02:03 -0700 Subject: [PATCH 02/10] cleanup --- JS/Variables.js | 1 - JS/index.js | 25 +++++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/JS/Variables.js b/JS/Variables.js index 8fa8ad0..29d0cf6 100644 --- a/JS/Variables.js +++ b/JS/Variables.js @@ -1,4 +1,3 @@ -// STRINGS TODO: make sure to seperate stuff that a user will want to input: BlueskyPDSName. // Mastodon export const MastodonWebsite = "mastodon_website"; export const MastodonClientID = "mastodon_client_id"; diff --git a/JS/index.js b/JS/index.js index 3255833..6814d3c 100644 --- a/JS/index.js +++ b/JS/index.js @@ -105,20 +105,13 @@ ArrowsButton[0].onclick = (event) => { // Call this to update the things :) async function PosterContainerUpdate() { let Lim = 6; - // Variables for the public timelines + // Variables for the public timelines. Only for mastodon let LocalVar = localStorage.getItem("Local"); var LocalTrue = (LocalVar === "true"); let RemoteVar = localStorage.getItem("Remote"); var RemoteTrue = (RemoteVar === "true"); let Website = localStorage.getItem(Variables.MastodonWebsite); - // Get the websites backwards. To see if I need more posts from Mastodon - let BlueskyTimeline; - if (localStorage.getItem(Variables.BlueskyPDS) == null) { - console.log("No Bluesky instance. multiplying mastodon posts by 2..."); - Lim = Lim * 2; - } else { - BlueskyTimeline = await BlueskyAPI.GetTimeline(); - } + // Mastodon gaining of timeline let MastodonTimeline; if (localStorage.getItem(Variables.MastodonWebsite) == null) { // The default website is Wetdry :3 @@ -126,15 +119,27 @@ async function PosterContainerUpdate() { } else { MastodonTimeline = await MastodonAPI.GetTimeline(); } + // Bluesky gaining of timeline + let BlueskyTimeline; + if (localStorage.getItem(Variables.BlueskyPDS) == null) { + console.log("No Bluesky instance. multiplying mastodon posts by 2..."); + Lim = Lim * 2; + } else { + BlueskyTimeline = await BlueskyAPI.GetTimeline(); + } + // Counters. The first counter counts the posts in the gained timelines... + // The second increments to another timeline once posts hit the "limit". let counter = 0; let countergroup = 0; let CenterContainer = document.getElementsByClassName("PostContainer")[2].children; for (let i in CenterContainer) { switch(countergroup) { + // Bsky case 0: CenterContainer[i].getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; - CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = BlueskyTimeline.feed[counter].post.author.handle; break; + // Mastodon case 1: CenterContainer[i].getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; -- 2.45.3 From 9feb38a9b4fa60cc79a5488299d8decc5d195483 Mon Sep 17 00:00:00 2001 From: CatAClock Date: Tue, 6 May 2025 16:30:35 -0700 Subject: [PATCH 03/10] bug fixes (finally) and framework for expanded posts --- CSS/expanded.css | 0 HTML/expanded.html | 22 ++++++++++++++++++++++ JS/expanded.js | 0 JS/index.js | 18 +++++++++++++----- 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 CSS/expanded.css create mode 100644 HTML/expanded.html create mode 100644 JS/expanded.js diff --git a/CSS/expanded.css b/CSS/expanded.css new file mode 100644 index 0000000..e69de29 diff --git a/HTML/expanded.html b/HTML/expanded.html new file mode 100644 index 0000000..b0985ae --- /dev/null +++ b/HTML/expanded.html @@ -0,0 +1,22 @@ + + + + The Fediverse + + + + + + + + + +
+

Nothing so far.

+

Bsky/Mastodon.

+

Text would go here

+

Image here if it is a thing

+

Back

+
+ + diff --git a/JS/expanded.js b/JS/expanded.js new file mode 100644 index 0000000..e69de29 diff --git a/JS/index.js b/JS/index.js index 6814d3c..dd21e59 100644 --- a/JS/index.js +++ b/JS/index.js @@ -8,6 +8,7 @@ import * as Variables from "./Variables.js"; let Warning = document.getElementsByClassName("WarningMessage")[0]; let Main = document.getElementsByClassName("MainBefore")[0]; let ContainerContainer = document.getElementsByClassName("PostContainerContainer")[0]; +let ClickableContainers = ContainerContainer.getElementsByClassName("PostContainer")[2].children; let BrowserWidth = window.innerWidth; let BrowserHeight = window.innerHeight; @@ -102,6 +103,13 @@ ArrowsButton[0].onclick = (event) => { PosterContainerUpdate(); } +for (let i of ClickableContainers) { + i.onclick = (event) => { + // Save some info + window.location.href = "./HTML/expanded.html"; + } +} + // Call this to update the things :) async function PosterContainerUpdate() { let Lim = 6; @@ -132,17 +140,17 @@ async function PosterContainerUpdate() { let counter = 0; let countergroup = 0; let CenterContainer = document.getElementsByClassName("PostContainer")[2].children; - for (let i in CenterContainer) { + for (let i of CenterContainer) { switch(countergroup) { // Bsky case 0: - CenterContainer[i].getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; - CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = BlueskyTimeline.feed[counter].post.author.handle; + i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; + i.getElementsByClassName("Username")[0].innerHTML = BlueskyTimeline.feed[counter].post.author.handle; break; // Mastodon case 1: - CenterContainer[i].getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; - CenterContainer[i].getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + i.getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; + i.getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; break; } counter = counter + 1; -- 2.45.3 From 6416b9bb2cd581997b9c0293fe17154d61c16d46 Mon Sep 17 00:00:00 2001 From: CatAClock Date: Tue, 6 May 2025 16:33:07 -0700 Subject: [PATCH 04/10] I saw a bug --- JS/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/JS/index.js b/JS/index.js index dd21e59..341ccdc 100644 --- a/JS/index.js +++ b/JS/index.js @@ -142,16 +142,16 @@ async function PosterContainerUpdate() { let CenterContainer = document.getElementsByClassName("PostContainer")[2].children; for (let i of CenterContainer) { switch(countergroup) { - // Bsky - case 0: - i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; - i.getElementsByClassName("Username")[0].innerHTML = BlueskyTimeline.feed[counter].post.author.handle; - break; // Mastodon - case 1: + case 0: i.getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; i.getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; break; + // Bsky + case 1: + i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; + i.getElementsByClassName("Username")[0].innerHTML = BlueskyTimeline.feed[counter].post.author.handle; + break; } counter = counter + 1; if (counter >= Lim) { -- 2.45.3 From cb62f5fecc0884d1b558c0ef0d5ed9fb8a12f556 Mon Sep 17 00:00:00 2001 From: CatAClock Date: Wed, 7 May 2025 16:18:26 -0700 Subject: [PATCH 05/10] added some additional goods. Now we just need the cursors to work --- JS/BlueskyAPI.js | 13 ++++++++++--- JS/MastodonAPI.js | 12 +++++++++--- JS/index.js | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/JS/BlueskyAPI.js b/JS/BlueskyAPI.js index 7d1e951..45d7e4e 100644 --- a/JS/BlueskyAPI.js +++ b/JS/BlueskyAPI.js @@ -1,13 +1,20 @@ import * as Variables from "./Variables.js"; -export async function GetTimeline() { +export async function GetTimeline(cursor) { if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { console.log("No access token!"); return ""; } let PDS = localStorage.getItem(Variables.BlueskyPDS); - let DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getTimeline"); - let request = fetch(PDS + "/xrpc/app.bsky.feed.getTimeline", {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + let DPoP; + let request; + if (cursor == "") { + DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getTimeline"); + request = fetch(PDS + "/xrpc/app.bsky.feed.getTimeline", {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + } else { + DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getTimeline?cursor=" + cursor); + request = fetch(PDS + "/xrpc/app.bsky.feed.getTimeline?cursor=" + cursor, {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + } let body = await request.then((response) => response.json()); let status = await request.then((response) => response.status); let header = await request.then((response) => response.headers.get("dpop-nonce")); diff --git a/JS/MastodonAPI.js b/JS/MastodonAPI.js index aefa1b5..80fe0e4 100644 --- a/JS/MastodonAPI.js +++ b/JS/MastodonAPI.js @@ -24,15 +24,21 @@ export async function GetPublicTimeline(Local = false, Remote = false, Website) return Timeline; } -export async function GetTimeline() { +// Get the personal timeline. +export async function GetTimeline(cursor) { if (localStorage.getItem(Variables.MastodonAccessToken) == null) { console.log("No access token!"); return ""; } let Website = localStorage.getItem(Variables.MastodonWebsite); // Get the varaibles that are stored in localStorage. - return await fetch(Website + "/api/v1/timelines/home", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) - .then((response) => response.json()); + if (cursor == "") { + return await fetch(Website + "/api/v1/timelines/home", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + .then((response) => response.json()); + } else { + return await fetch(Website + "/api/v1/timelines/home?min_id=" + cursor, {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + .then((response) => response.json()); + } } // Gets the favorites of a user that you have access to. diff --git a/JS/index.js b/JS/index.js index 341ccdc..2400292 100644 --- a/JS/index.js +++ b/JS/index.js @@ -110,6 +110,9 @@ for (let i of ClickableContainers) { } } +// These numbers are here so you don't go back. +let MastodonPostNumber = ""; +let BlueskyPostNumber = ""; // Call this to update the things :) async function PosterContainerUpdate() { let Lim = 6; @@ -125,7 +128,7 @@ async function PosterContainerUpdate() { // The default website is Wetdry :3 MastodonTimeline = await MastodonAPI.GetPublicTimeline(LocalTrue, RemoteTrue, "https://wetdry.world"); } else { - MastodonTimeline = await MastodonAPI.GetTimeline(); + MastodonTimeline = await MastodonAPI.GetTimeline(MastodonPostNumber); } // Bluesky gaining of timeline let BlueskyTimeline; @@ -133,24 +136,39 @@ async function PosterContainerUpdate() { console.log("No Bluesky instance. multiplying mastodon posts by 2..."); Lim = Lim * 2; } else { - BlueskyTimeline = await BlueskyAPI.GetTimeline(); + BlueskyTimeline = await BlueskyAPI.GetTimeline(BlueskyPostNumber); } + // Debugging. + console.log(MastodonTimeline); + console.log(BlueskyTimeline); // Counters. The first counter counts the posts in the gained timelines... // The second increments to another timeline once posts hit the "limit". let counter = 0; let countergroup = 0; let CenterContainer = document.getElementsByClassName("PostContainer")[2].children; + // This var is meant to stop "already seen" posts. + let BlueskyPostsDup = []; for (let i of CenterContainer) { switch(countergroup) { // Mastodon case 0: - i.getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; - i.getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + // In the case of a "reblog"... + if (MastodonTimeline[counter].reblog == true) { + i.getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; + i.getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + console.log("reblogged post"); // We need to make sure the user knows this... later. + } else { + i.getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; + i.getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + } break; // Bsky case 1: + // check for "already seen" posts. + BlueskyTimeline = CheckForDups(BlueskyTimeline, BlueskyPostsDup, counter); i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; i.getElementsByClassName("Username")[0].innerHTML = BlueskyTimeline.feed[counter].post.author.handle; + BlueskyPostsDup.push(BlueskyTimeline.feed[counter].post.uri); break; } counter = counter + 1; @@ -161,6 +179,17 @@ async function PosterContainerUpdate() { } } +// Assistant function to help with checking for duplicates. +function CheckForDups(Timeline, DupeArray, counter) { + for (let j of DupeArray) { + if (j == Timeline.feed[counter].post.uri) { + Timeline.feed.splice(counter, 1); + Timeline = CheckForDups(Timeline, DupeArray, counter); + } + } + return Timeline; +} + // Open the settings SettingButton.onclick = (event) => { window.location.href = "./HTML/setting.html"; -- 2.45.3 From fd30565eebd27bc3fcd691a765a55b38933d2eb1 Mon Sep 17 00:00:00 2001 From: CatAClock Date: Wed, 7 May 2025 22:09:51 -0700 Subject: [PATCH 06/10] past & future done, saving bandwidth as well --- JS/BlueskyAPI.js | 42 ++++++++++------------ JS/MastodonAPI.js | 6 ++-- JS/index.js | 89 +++++++++++++++++++++++++++-------------------- 3 files changed, 74 insertions(+), 63 deletions(-) diff --git a/JS/BlueskyAPI.js b/JS/BlueskyAPI.js index 45d7e4e..bab5990 100644 --- a/JS/BlueskyAPI.js +++ b/JS/BlueskyAPI.js @@ -1,31 +1,27 @@ import * as Variables from "./Variables.js"; -export async function GetTimeline(cursor) { +export async function GetTimeline(Cursor) { if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { console.log("No access token!"); return ""; } + let PDS = localStorage.getItem(Variables.BlueskyPDS); let DPoP; let request; - if (cursor == "") { + if (Cursor == "") { DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getTimeline"); request = fetch(PDS + "/xrpc/app.bsky.feed.getTimeline", {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); } else { - DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getTimeline?cursor=" + cursor); - request = fetch(PDS + "/xrpc/app.bsky.feed.getTimeline?cursor=" + cursor, {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getTimeline?cursor=" + Cursor); + request = fetch(PDS + "/xrpc/app.bsky.feed.getTimeline?cursor=" + Cursor, {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); } let body = await request.then((response) => response.json()); let status = await request.then((response) => response.status); let header = await request.then((response) => response.headers.get("dpop-nonce")); if (status == 401) { - if (body.message.includes("DPoP nonce mismatch")) { - await localStorage.setItem(Variables.BlueskyNonce, header); - } - if (body.message.includes("claim timestamp check failed")) { - await RefreshTokens(); - } - body = await GetTimeline(); + await HandleError(body, header); + body = await GetTimeline(Cursor); } return body; } @@ -53,12 +49,7 @@ export async function CreatePost(DID, Text) { let status = await request.then((response) => response.status); let header = await request.then((response) => response.headers.get("dpop-nonce")); if (status == 401) { - if (body.message.includes("DPoP nonce mismatch")) { - await localStorage.setItem(Variables.BlueskyNonce, header); - } - if (body.message.includes("claim timestamp check failed")) { - await RefreshTokens(); - } + await HandleError(body, header); body = await CreatePost(DID, Text); } return body; @@ -69,6 +60,7 @@ export async function SetThreadGate(DID, Post, VisibilitySettings) { console.log("No access token!"); return ""; } + let PDS = localStorage.getItem(Variables.BlueskyPDS); let Json = { "$type": "app.bsky.feed.threadgate", @@ -88,12 +80,7 @@ export async function SetThreadGate(DID, Post, VisibilitySettings) { let status = await request.then((response) => response.status); let header = await request.then((response) => response.headers.get("dpop-nonce")); if (status == 401) { - if (body.message.includes("DPoP nonce mismatch")) { - await localStorage.setItem(Variables.BlueskyNonce, header); - } - if (body.message.includes("claim timestamp check failed")) { - await RefreshTokens(); - } + await HandleError(body, header); body = await SetThreadGate(DID, Post, VisibilitySettings); } return body; @@ -290,6 +277,15 @@ export async function RefreshTokens() { localStorage.setItem(Variables.BlueskyRefreshToken, body.refresh_token); } +async function HandleError(body, header) { + if (body.message.includes("DPoP nonce mismatch")) { + await localStorage.setItem(Variables.BlueskyNonce, header); + } + if (body.message.includes("claim timestamp check failed")) { + await RefreshTokens(); + } +} + // Stolen from elsewhere. // Firefox snippet; Slightly edited. async function sha256(message) { diff --git a/JS/MastodonAPI.js b/JS/MastodonAPI.js index 80fe0e4..926db2b 100644 --- a/JS/MastodonAPI.js +++ b/JS/MastodonAPI.js @@ -25,18 +25,18 @@ export async function GetPublicTimeline(Local = false, Remote = false, Website) } // Get the personal timeline. -export async function GetTimeline(cursor) { +export async function GetTimeline(Cursor) { if (localStorage.getItem(Variables.MastodonAccessToken) == null) { console.log("No access token!"); return ""; } let Website = localStorage.getItem(Variables.MastodonWebsite); // Get the varaibles that are stored in localStorage. - if (cursor == "") { + if (Cursor == "") { return await fetch(Website + "/api/v1/timelines/home", {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) .then((response) => response.json()); } else { - return await fetch(Website + "/api/v1/timelines/home?min_id=" + cursor, {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) + return await fetch(Website + "/api/v1/timelines/home?max_id=" + Cursor, {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) .then((response) => response.json()); } } diff --git a/JS/index.js b/JS/index.js index 2400292..4b3d502 100644 --- a/JS/index.js +++ b/JS/index.js @@ -82,12 +82,11 @@ ArrowsButton[1].onclick = (event) => { if (!ContainerContainer.classList.contains("NextAnimation")) { ContainerContainer.classList.add("NextAnimation"); ButtonSound.play(); - console.log("loading next posts..."); setTimeout(() => { ContainerContainer.classList.remove("NextAnimation"); }, 1000); } - PosterContainerUpdate(); + PosterContainerUpdate("Forward"); } // Clicking the back button @@ -95,12 +94,11 @@ ArrowsButton[0].onclick = (event) => { if (!ContainerContainer.classList.contains("BackAnimation")) { ContainerContainer.classList.add("BackAnimation"); ButtonSound.play(); - console.log("getting previous posts..."); setTimeout(() => { ContainerContainer.classList.remove("BackAnimation"); }, 1000); } - PosterContainerUpdate(); + PosterContainerUpdate("Backward"); } for (let i of ClickableContainers) { @@ -111,40 +109,47 @@ for (let i of ClickableContainers) { } // These numbers are here so you don't go back. -let MastodonPostNumber = ""; -let BlueskyPostNumber = ""; +let MastodonLoadedFeed = []; +let BlueskyLoadedFeed = []; +let CurrentThing = 0; + // Call this to update the things :) -async function PosterContainerUpdate() { +async function PosterContainerUpdate(Direction) { let Lim = 6; - // Variables for the public timelines. Only for mastodon - let LocalVar = localStorage.getItem("Local"); - var LocalTrue = (LocalVar === "true"); - let RemoteVar = localStorage.getItem("Remote"); - var RemoteTrue = (RemoteVar === "true"); let Website = localStorage.getItem(Variables.MastodonWebsite); - // Mastodon gaining of timeline - let MastodonTimeline; + + let Mastodon = true; + + // Mastodon gaining of timeline. if (localStorage.getItem(Variables.MastodonWebsite) == null) { // The default website is Wetdry :3 - MastodonTimeline = await MastodonAPI.GetPublicTimeline(LocalTrue, RemoteTrue, "https://wetdry.world"); - } else { - MastodonTimeline = await MastodonAPI.GetTimeline(MastodonPostNumber); + console.log("No Mastodon instance. multiplying mastodon posts by 2..."); + Lim = Lim * 2; + Mastodon = false; + } else if (MastodonLoadedFeed == 0) { + MastodonLoadedFeed = await MastodonAPI.GetTimeline(""); } - // Bluesky gaining of timeline - let BlueskyTimeline; + // Bluesky gaining of timeline. if (localStorage.getItem(Variables.BlueskyPDS) == null) { console.log("No Bluesky instance. multiplying mastodon posts by 2..."); Lim = Lim * 2; - } else { - BlueskyTimeline = await BlueskyAPI.GetTimeline(BlueskyPostNumber); + } else if (BlueskyLoadedFeed.length == 0) { + BlueskyLoadedFeed = await BlueskyAPI.GetTimeline("").then((response) => response.feed); } - // Debugging. - console.log(MastodonTimeline); - console.log(BlueskyTimeline); - // Counters. The first counter counts the posts in the gained timelines... - // The second increments to another timeline once posts hit the "limit". + if (Direction == "Forward") { + CurrentThing = CurrentThing + Lim; + } else if (Direction == "Backward") { + if (CurrentThing - Lim >= 0) { + CurrentThing = CurrentThing - Lim; + } + } + // The first one is for counting. + // The countergroup increments to another timeline once posts hit the "limit". let counter = 0; let countergroup = 0; + if (Mastodon == false) { + countergroup = countergroup + 1; + } let CenterContainer = document.getElementsByClassName("PostContainer")[2].children; // This var is meant to stop "already seen" posts. let BlueskyPostsDup = []; @@ -152,23 +157,33 @@ async function PosterContainerUpdate() { switch(countergroup) { // Mastodon case 0: + if (MastodonLoadedFeed[CurrentThing + counter] == undefined) { + let TempFeed = await MastodonAPI.GetTimeline(MastodonLoadedFeed[CurrentThing + counter - 1].id); + MastodonLoadedFeed = MastodonLoadedFeed.concat(TempFeed); + } // In the case of a "reblog"... - if (MastodonTimeline[counter].reblog == true) { - i.getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; - i.getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + if (MastodonLoadedFeed[counter].reblog == true) { + i.getElementsByClassName("PostContent")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].content; + i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username; console.log("reblogged post"); // We need to make sure the user knows this... later. } else { - i.getElementsByClassName("PostContent")[0].innerHTML = MastodonTimeline[counter].content; - i.getElementsByClassName("Username")[0].innerHTML = MastodonTimeline[counter].account.username; + i.getElementsByClassName("PostContent")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].content; + i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username; } break; // Bsky case 1: + if (BlueskyLoadedFeed[CurrentThing + counter] == undefined) { + let TempFeed = await BlueskyAPI.GetTimeline(BlueskyLoadedFeed[CurrentThing + counter - 1].indexedAt).then((response) => response.feed) + BlueskyLoadedFeed = BlueskyLoadedFeed.concat(TempFeed); + } // check for "already seen" posts. - BlueskyTimeline = CheckForDups(BlueskyTimeline, BlueskyPostsDup, counter); - i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyTimeline.feed[counter].post.record.text; - i.getElementsByClassName("Username")[0].innerHTML = BlueskyTimeline.feed[counter].post.author.handle; - BlueskyPostsDup.push(BlueskyTimeline.feed[counter].post.uri); + BlueskyLoadedFeed = CheckForDups(BlueskyLoadedFeed, BlueskyPostsDup, counter); + i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.record.text; + i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle; + // TODO: This can cause major lag unless properly handled. + BlueskyPostsDup.push(BlueskyLoadedFeed[counter].post.uri); + // Direction for where you go :3 break; } counter = counter + 1; @@ -182,8 +197,8 @@ async function PosterContainerUpdate() { // Assistant function to help with checking for duplicates. function CheckForDups(Timeline, DupeArray, counter) { for (let j of DupeArray) { - if (j == Timeline.feed[counter].post.uri) { - Timeline.feed.splice(counter, 1); + if (j == Timeline[counter].post.uri) { + Timeline.splice(counter, 1); Timeline = CheckForDups(Timeline, DupeArray, counter); } } -- 2.45.3 From 06203980a2179786eab1b97f0b2dc14407143cae Mon Sep 17 00:00:00 2001 From: CatAClock Date: Thu, 8 May 2025 12:29:06 -0700 Subject: [PATCH 07/10] Reblogs are now seen, solved the overflow problem --- CSS/index.css | 8 +++++++- JS/index.js | 20 ++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CSS/index.css b/CSS/index.css index ee4f788..47d879f 100644 --- a/CSS/index.css +++ b/CSS/index.css @@ -178,7 +178,9 @@ html { height: 70vh; } -.Post { +.Post { + overflow: hidden; + border-style: solid; border-width: 2px; @@ -186,6 +188,10 @@ html { height: 25%; } +.Post:hover { + overflow: scroll; +} + .Username { text-align: center; font-weight: bold; diff --git a/JS/index.js b/JS/index.js index 4b3d502..9054640 100644 --- a/JS/index.js +++ b/JS/index.js @@ -143,6 +143,9 @@ async function PosterContainerUpdate(Direction) { CurrentThing = CurrentThing - Lim; } } + // Debugging + console.log(MastodonLoadedFeed); + console.log(BlueskyLoadedFeed); // The first one is for counting. // The countergroup increments to another timeline once posts hit the "limit". let counter = 0; @@ -162,10 +165,9 @@ async function PosterContainerUpdate(Direction) { MastodonLoadedFeed = MastodonLoadedFeed.concat(TempFeed); } // In the case of a "reblog"... - if (MastodonLoadedFeed[counter].reblog == true) { - i.getElementsByClassName("PostContent")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].content; - i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username; - console.log("reblogged post"); // We need to make sure the user knows this... later. + if (MastodonLoadedFeed[CurrentThing + counter].reblog != null) { + i.getElementsByClassName("PostContent")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].reblog.content; + i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].reblog.account.username + "(R: " + MastodonLoadedFeed[CurrentThing + counter].account.username + " )"; } else { i.getElementsByClassName("PostContent")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].content; i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username; @@ -179,8 +181,13 @@ async function PosterContainerUpdate(Direction) { } // check for "already seen" posts. BlueskyLoadedFeed = CheckForDups(BlueskyLoadedFeed, BlueskyPostsDup, counter); - i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.record.text; - i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle; + if (BlueskyLoadedFeed[CurrentThing + counter].hasOwnProperty("reason") && BlueskyLoadedFeed[CurrentThing + counter].reason.$type == "app.bsky.feed.defs#reasonRepost") { + i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.record.text; + i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle + "(R: " + BlueskyLoadedFeed[CurrentThing + counter].reason.by.handle + " )"; + } else { + i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.record.text; + i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle; + } // TODO: This can cause major lag unless properly handled. BlueskyPostsDup.push(BlueskyLoadedFeed[counter].post.uri); // Direction for where you go :3 @@ -199,6 +206,7 @@ function CheckForDups(Timeline, DupeArray, counter) { for (let j of DupeArray) { if (j == Timeline[counter].post.uri) { Timeline.splice(counter, 1); + console.log("Culled a duplicate post.") Timeline = CheckForDups(Timeline, DupeArray, counter); } } -- 2.45.3 From ba80949fed529729c126a62e3a5c39187bbd5a0b Mon Sep 17 00:00:00 2001 From: CatAClock Date: Thu, 8 May 2025 13:33:21 -0700 Subject: [PATCH 08/10] fixed up an HTML file & made content warnings --- JS/index.js | 34 ++++++--- index.html | 193 ++++++++++++++++++++++++++-------------------------- 2 files changed, 122 insertions(+), 105 deletions(-) diff --git a/JS/index.js b/JS/index.js index 9054640..1a0b938 100644 --- a/JS/index.js +++ b/JS/index.js @@ -85,8 +85,8 @@ ArrowsButton[1].onclick = (event) => { setTimeout(() => { ContainerContainer.classList.remove("NextAnimation"); }, 1000); + PosterContainerUpdate("Forward"); } - PosterContainerUpdate("Forward"); } // Clicking the back button @@ -97,8 +97,8 @@ ArrowsButton[0].onclick = (event) => { setTimeout(() => { ContainerContainer.classList.remove("BackAnimation"); }, 1000); + PosterContainerUpdate("Backward"); } - PosterContainerUpdate("Backward"); } for (let i of ClickableContainers) { @@ -164,10 +164,16 @@ async function PosterContainerUpdate(Direction) { let TempFeed = await MastodonAPI.GetTimeline(MastodonLoadedFeed[CurrentThing + counter - 1].id); MastodonLoadedFeed = MastodonLoadedFeed.concat(TempFeed); } - // In the case of a "reblog"... + // Content warning. Don't show the content unless clicked!!! + if (MastodonLoadedFeed[CurrentThing + counter].spoiler_text != "") { + i.getElementsByClassName("PostContent")[0].innerHTML = "WARNING: " + MastodonLoadedFeed[CurrentThing + counter].spoiler_text + ""; + i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username; + break; + } + // Check for a reblog. if (MastodonLoadedFeed[CurrentThing + counter].reblog != null) { i.getElementsByClassName("PostContent")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].reblog.content; - i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].reblog.account.username + "(R: " + MastodonLoadedFeed[CurrentThing + counter].account.username + " )"; + i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].reblog.account.username + " ( R: " + MastodonLoadedFeed[CurrentThing + counter].account.username + " )"; } else { i.getElementsByClassName("PostContent")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].content; i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username; @@ -176,20 +182,30 @@ async function PosterContainerUpdate(Direction) { // Bsky case 1: if (BlueskyLoadedFeed[CurrentThing + counter] == undefined) { - let TempFeed = await BlueskyAPI.GetTimeline(BlueskyLoadedFeed[CurrentThing + counter - 1].indexedAt).then((response) => response.feed) + let TempFeed = await BlueskyAPI.GetTimeline(BlueskyLoadedFeed[CurrentThing + counter - 1].post.indexedAt).then((response) => response.feed) BlueskyLoadedFeed = BlueskyLoadedFeed.concat(TempFeed); } - // check for "already seen" posts. + // check for "already seen" posts, then put it as a seen post. BlueskyLoadedFeed = CheckForDups(BlueskyLoadedFeed, BlueskyPostsDup, counter); + BlueskyPostsDup.push(BlueskyLoadedFeed[counter].post.uri); + // Labels + if (BlueskyLoadedFeed[CurrentThing + counter].post.labels.length != 0) { + i.getElementsByClassName("PostContent")[0].innerHTML = "LABELS APPLIED: "; + i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle; + for (let lab of BlueskyLoadedFeed[CurrentThing + counter].post.labels) { + i.getElementsByClassName("PostContent")[0].innerHTML = i.getElementsByClassName("PostContent")[0].innerHTML + lab.val + " "; + } + i.getElementsByClassName("PostContent")[0].innerHTML = i.getElementsByClassName("PostContent")[0].innerHTML + ""; + break; + } + // Check for a reblog if (BlueskyLoadedFeed[CurrentThing + counter].hasOwnProperty("reason") && BlueskyLoadedFeed[CurrentThing + counter].reason.$type == "app.bsky.feed.defs#reasonRepost") { i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.record.text; - i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle + "(R: " + BlueskyLoadedFeed[CurrentThing + counter].reason.by.handle + " )"; + i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle + " ( R: " + BlueskyLoadedFeed[CurrentThing + counter].reason.by.handle + " )"; } else { i.getElementsByClassName("PostContent")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.record.text; i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle; } - // TODO: This can cause major lag unless properly handled. - BlueskyPostsDup.push(BlueskyLoadedFeed[counter].post.uri); // Direction for where you go :3 break; } diff --git a/index.html b/index.html index e80d956..23e16bb 100644 --- a/index.html +++ b/index.html @@ -33,255 +33,256 @@
-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

No

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

+
-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-

Testing

-

The things

+

+

-- 2.45.3 From e463d16d4103f21e755a387ff5fc85474cda9c83 Mon Sep 17 00:00:00 2001 From: CatAClock Date: Thu, 8 May 2025 14:37:39 -0700 Subject: [PATCH 09/10] replies longer than the allowed character limit now get seperated. --- JS/BlueskyAPI.js | 59 +++++++++++++++++++++++++++++++++++++++++++++++ JS/MastodonAPI.js | 36 +++++++++++++++++++++++++++-- JS/post.js | 3 +++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/JS/BlueskyAPI.js b/JS/BlueskyAPI.js index bab5990..22cca87 100644 --- a/JS/BlueskyAPI.js +++ b/JS/BlueskyAPI.js @@ -52,6 +52,65 @@ export async function CreatePost(DID, Text) { await HandleError(body, header); body = await CreatePost(DID, Text); } + if (status == 400) { + let matches = body.message.match(/(\d+)/); + Json.text = Text.slice(0, matches[0] - 1); + RequestBody.record = Json; + DPoP = await ClientDPoPPDS("POST", PDS + "/xrpc/com.atproto.repo.createRecord"); + request = fetch(PDS + "/xrpc/com.atproto.repo.createRecord", { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + body = await request.then((response) => response.json()); + status = await request.then((response) => response.status); + await CreateReplyPost(DID, Text.slice(matches[0] - 1, Text.length - 1), body, body); + } + return body; +} + +export async function CreateReplyPost(DID, Text, ReplyID, RootID) { + if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { + console.log("No access token!"); + return ""; + } + + let PDS = localStorage.getItem(Variables.BlueskyPDS); + let Json = { + "$type": "app.bsky.feed.post", + "text": Text, + "createdAt": new Date(Date.now()).toISOString(), + "reply": { + "root": { + "uri": ReplyID.uri, + "cid": ReplyID.cid + }, + "parent": { + "uri": RootID.uri, + "cid": RootID.cid + } + } + } + let RequestBody = { + "repo": DID, + "collection": "app.bsky.feed.post", + "record": Json + } + let DPoP = await ClientDPoPPDS("POST", PDS + "/xrpc/com.atproto.repo.createRecord"); + let request = fetch(PDS + "/xrpc/com.atproto.repo.createRecord", { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + let body = await request.then((response) => response.json()); + let status = await request.then((response) => response.status); + let header = await request.then((response) => response.headers.get("dpop-nonce")); + if (status == 401) { + await HandleError(body, header); + body = await CreatePost(DID, Text); + } + if (status == 400) { + let matches = body.message.match(/(\d+)/); + Json.text = Text.slice(0, matches[0] - 1); + RequestBody.record = Json; + let DPoP = await ClientDPoPPDS("POST", PDS + "/xrpc/com.atproto.repo.createRecord"); + let request = fetch(PDS + "/xrpc/com.atproto.repo.createRecord", { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + let body = await request.then((response) => response.json()); + let status = await request.then((response) => response.status); + await CreateReplyPost(DID, Text.slice(matches[0] - 1, Text.length - 1), body, RootID); + } return body; } diff --git a/JS/MastodonAPI.js b/JS/MastodonAPI.js index 926db2b..887f228 100644 --- a/JS/MastodonAPI.js +++ b/JS/MastodonAPI.js @@ -87,8 +87,40 @@ export async function CreateStatus(Text, Visibility = "public") { Text = Text.replace(/(?:\r|\n|\r\n)/g, '
'); // Check for a token let Website = localStorage.getItem(Variables.MastodonWebsite); - return await fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}) - .then((response) => response.json()); + let request = fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}); + let body = await request.then((response) => response.json()); + let status = await request.then((response) => response.status); + // This is in case you went over the characters. + if (status == 422) { + let matches = body.error.match(/(\d+)/); + console.log(matches); + request = fetch(Website + "/api/v1/statuses?status=" + Text.slice(0, matches[0] - 1) + "&visibility=" + Visibility, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}); + body = await request.then((response) => response.json()); + await CreateReplyStatus(Text.slice(matches[0] - 1, Text.length - 1), Visibility, body.id); + } + return body; +} + +export async function CreateReplyStatus(Text, Visibility = "public", ReplyID) { + if (localStorage.getItem(Variables.MastodonAccessToken) == null) { + console.log("No access token!"); + return ""; + } + // Stolen from StackOverflow + Text = Text.replace(/(?:\r|\n|\r\n)/g, '
'); + // Check for a token + let Website = localStorage.getItem(Variables.MastodonWebsite); + let request = fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility + "&in_reply_to_id=" + ReplyID, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}); + let body = await request.then((response) => response.json()); + let status = await request.then((response) => response.status); + // This is in case you went over the characters. + if (status == 422) { + let matches = body.error.match(/(\d+)/); + request = fetch(Website + "/api/v1/statuses?status=" + Text.slice(0, matches[0] - 1) + "&visibility=" + Visibility + "&in_reply_to_id=" + ReplyID, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}}); + body = await request.then((response) => response.json()); + await CreateReplyStatus(Text.slice(matches[0] - 1, Text.length - 1), Visibility, body.id); + } + return body; } // The first step to using the app. diff --git a/JS/post.js b/JS/post.js index d729be6..3598f7c 100644 --- a/JS/post.js +++ b/JS/post.js @@ -8,6 +8,9 @@ let PostButton = document.getElementsByClassName("Button")[0]; let VisibilityDropdown = document.getElementsByClassName("PostVisibility")[0]; let InputArea = document.getElementsByClassName("Text")[0]; +// Extra Variables + + // Clicking the beeg POST button. PostButton.onclick = (event) => { Post(); -- 2.45.3 From a59005da83af94c5e3bca56a8b26353b97babbd7 Mon Sep 17 00:00:00 2001 From: CatAClock Date: Thu, 8 May 2025 14:58:48 -0700 Subject: [PATCH 10/10] The details are now viewable in extended view --- HTML/expanded.html | 8 ++++---- JS/expanded.js | 5 +++++ JS/index.js | 7 ++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/HTML/expanded.html b/HTML/expanded.html index b0985ae..403a957 100644 --- a/HTML/expanded.html +++ b/HTML/expanded.html @@ -12,10 +12,10 @@
-

Nothing so far.

-

Bsky/Mastodon.

-

Text would go here

-

Image here if it is a thing

+

+

Bsky/Mastodon (TODO)

+

+

Image here if it is a thing (TODO)

Back

diff --git a/JS/expanded.js b/JS/expanded.js index e69de29..c4838cd 100644 --- a/JS/expanded.js +++ b/JS/expanded.js @@ -0,0 +1,5 @@ +let content = decodeURI(document.location.href.split("content=")[1]); +let username = decodeURI(document.location.href.split("username=")[1].split("&content")[0]); + +document.getElementsByClassName("Handle")[0].innerHTML = username; +document.getElementsByClassName("DetailedText")[0].innerHTML = content; diff --git a/JS/index.js b/JS/index.js index 1a0b938..d19b711 100644 --- a/JS/index.js +++ b/JS/index.js @@ -103,8 +103,10 @@ ArrowsButton[0].onclick = (event) => { for (let i of ClickableContainers) { i.onclick = (event) => { + let content = i.getElementsByClassName("PostContent")[0].innerHTML + let username = i.getElementsByClassName("Username")[0].innerHTML // Save some info - window.location.href = "./HTML/expanded.html"; + window.location.href = "./HTML/expanded.html?username=" + username + "&content=" + content; } } @@ -153,10 +155,9 @@ async function PosterContainerUpdate(Direction) { if (Mastodon == false) { countergroup = countergroup + 1; } - let CenterContainer = document.getElementsByClassName("PostContainer")[2].children; // This var is meant to stop "already seen" posts. let BlueskyPostsDup = []; - for (let i of CenterContainer) { + for (let i of ClickableContainers) { switch(countergroup) { // Mastodon case 0: -- 2.45.3