diff --git a/CSS/index.css b/CSS/index.css index 1207c5b..f35e0a3 100644 --- a/CSS/index.css +++ b/CSS/index.css @@ -216,7 +216,7 @@ html { } /* Footer things */ -.Setting, .Posting, .Mail { +.Setting, .Posting, .Mail, .Feed { margin-top: 30px; border-style: solid; @@ -226,12 +226,12 @@ html { height: 100px; } -.Time { - font-size: 4ch; +.Mail, .Setting { + margin-right: 5%; } -.Posting { - margin-right: 5%; +.Time { + font-size: 4ch; } .MainFooter { diff --git a/HTML/setting.html b/HTML/setting.html index 6a25e31..a5fa7ab 100644 --- a/HTML/setting.html +++ b/HTML/setting.html @@ -17,7 +17,6 @@
-

Toggle Local

Toggle Remote

diff --git a/JS/BlueskyAPI.js b/JS/BlueskyAPI.js index e979cb7..fb04879 100644 --- a/JS/BlueskyAPI.js +++ b/JS/BlueskyAPI.js @@ -9,15 +9,14 @@ export async function GetTimeline(Cursor) { return ""; } - let PDS = localStorage.getItem(Variables.BlueskyPDS); 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}}); + DPoP = await ClientDPoPPDS("GET", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getTimeline"); + request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/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", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getTimeline?cursor=" + Cursor); + request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/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); @@ -29,6 +28,32 @@ export async function GetTimeline(Cursor) { return body; } +// Gets a "public" timeline (essentially a feed by bsky.app where you can discover posts). +export async function GetPublicTimeline(Cursor) { + if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { + console.log("No access token!"); + return ""; + } + + let DPoP; + let request; + if (Cursor == "") { + DPoP = await ClientDPoPPDS("GET", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot"); + request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot", {method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + } else { + DPoP = await ClientDPoPPDS("GET", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getTimeline?feed=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot&cursor=" + Cursor); + request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getTimeline?feed=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot&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) { + await HandleError(body, header); + body = await GetFeed(Cursor); + } + return body; +} + // This gets the post. If there are multiple URIs, they must be within an array. export async function GetPosts(URIs) { if (localStorage.getItem(Variables.BlueskyAccessToken) == null) { @@ -36,9 +61,8 @@ export async function GetPosts(URIs) { return ""; } - let PDS = localStorage.getItem(Variables.BlueskyPDS); - let DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/app.bsky.feed.getPosts?uris=" + URIs); - let request = fetch(PDS + "/xrpc/app.bsky.feed.getPosts?uris=" + URIs, { method: "GET", headers: {"Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}}); + let DPoP = await ClientDPoPPDS("GET", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getPosts?uris=" + URIs); + let request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getPosts?uris=" + URIs, { 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")); @@ -220,10 +244,10 @@ export async function CreateRecord(Repo, Collection, Record, RKey) { // Applyers export function ApplyFacets(record, text) { - var StringArray = []; - var SplitAreas = [0]; - var Hrefs = []; - var TempText = ""; + let StringArray = []; + let SplitAreas = [0]; + let Hrefs = []; + let TempText = ""; if (record.hasOwnProperty("facets")) { // First, append split areas. for (let i of record.facets) { @@ -237,9 +261,9 @@ export function ApplyFacets(record, text) { // Last minute append. SplitAreas.push(text.length); // Remove emoji regex - var EmojiRegex = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu; - var EmojiObjects = text.match(EmojiRegex); - var SubtractNumber = 0; + let EmojiRegex = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu; + let EmojiObjects = text.match(EmojiRegex); + let SubtractNumber = 0; if (EmojiObjects != null) { SubtractNumber = EmojiObjects.length * 2; } @@ -327,7 +351,7 @@ export async function ClientDPoPToken(POSTorGET, RequestURL) { let PrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(localStorage.getItem(Variables.BlueskyPrivateKey)), {name: "ECDSA", namedCurve: "P-256"}, true, ["sign"]); // Header - var Header = {typ: "dpop+jwt", alg: "ES256", jwk: + let Header = {typ: "dpop+jwt", alg: "ES256", jwk: await crypto.subtle.exportKey("jwk", PublicKey) .then(function(response) { delete response["key_ops"]; @@ -336,7 +360,7 @@ export async function ClientDPoPToken(POSTorGET, RequestURL) { return response}) }; // Payload - var Payload = {}; + let Payload = {}; Payload.iss = "https://fedi.crowdedgames.group/oauth/client-metadata.json"; Payload.jti = crypto.randomUUID(); Payload.htm = POSTorGET; @@ -344,9 +368,9 @@ export async function ClientDPoPToken(POSTorGET, RequestURL) { Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000); Payload.nonce = localStorage.getItem(Variables.BlueskyNonce); - var sHeader = JSON.stringify(Header); - var sPayload = JSON.stringify(Payload); - var JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, + let sHeader = JSON.stringify(Header); + let sPayload = JSON.stringify(Payload); + let JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, await crypto.subtle.exportKey("jwk", PrivateKey) .then(function(response) { delete response["key_ops"]; @@ -362,7 +386,7 @@ export async function ClientDPoPPDS(POSTorGET, RequestURL) { let PrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(localStorage.getItem(Variables.BlueskyPrivateKey)), {name: "ECDSA", namedCurve: "P-256"}, true, ["sign"]); // Header - var Header = {typ: "dpop+jwt", alg: "ES256", jwk: + let Header = {typ: "dpop+jwt", alg: "ES256", jwk: await crypto.subtle.exportKey("jwk", PublicKey) .then(function(response) { delete response["key_ops"]; @@ -371,7 +395,7 @@ export async function ClientDPoPPDS(POSTorGET, RequestURL) { return response}) }; // Payload - var Payload = {}; + let Payload = {}; Payload.iss = "https://fedi.crowdedgames.group/oauth/client-metadata.json"; Payload.jti = crypto.randomUUID(); Payload.htm = POSTorGET; @@ -380,9 +404,9 @@ export async function ClientDPoPPDS(POSTorGET, RequestURL) { Payload.nonce = localStorage.getItem(Variables.BlueskyNonce); Payload.ath = await CreatePKCECodeChallenge(localStorage.getItem(Variables.BlueskyAccessToken)); - var sHeader = JSON.stringify(Header); - var sPayload = JSON.stringify(Payload); - var JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, + let sHeader = JSON.stringify(Header); + let sPayload = JSON.stringify(Payload); + let JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, await crypto.subtle.exportKey("jwk", PrivateKey) .then(function(response) { delete response["key_ops"]; diff --git a/JS/MastodonAPI.js b/JS/MastodonAPI.js index a136993..a4f00be 100644 --- a/JS/MastodonAPI.js +++ b/JS/MastodonAPI.js @@ -4,23 +4,32 @@ export const Scopes = "read write follow push"; // Getters // Gets the public timeline. -export async function GetPublicTimeline(Local = false, Remote = false, Website) { +export async function GetPublicTimeline(Local = false, Remote = false, Website, Cursor) { // Variables can be found in `setting.js` let Timeline; - if (Local == true && Remote == true) { - console.error("Don't set both Local and Remote timelines to true."); - return Timeline; - } - if (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?remote=true") - .then((response) => response.json()); + if (Cursor == "") { + if (Remote == true && Local == false) { + Timeline = await fetch(Website + "/api/v1/timelines/public?remote=true") + .then((response) => response.json()); + } else if (Local == true && Remote == false) { + Timeline = await fetch(Website + "/api/v1/timelines/public?local=true") + .then((response) => response.json()); + } else { + Timeline = await fetch(Website + "/api/v1/timelines/public") + .then((response) => response.json()); + } } else { - Timeline = await fetch(Website + "/api/v1/timelines/public") - .then((response) => response.json()); + if (Remote == true && Local == false) { + Timeline = await fetch(Website + "/api/v1/timelines/public?remote=true&max_id=" + Cursor) + .then((response) => response.json()); + } else if (Local == true && Remote == false) { + Timeline = await fetch(Website + "/api/v1/timelines/public?local=true&max_id=" + Cursor) + .then((response) => response.json()); + } else { + Timeline = await fetch(Website + "/api/v1/timelines/public?max_id=" + Cursor) + .then((response) => response.json()); + } } return Timeline; } diff --git a/JS/index.js b/JS/index.js index 8beb3db..f9d7ee4 100644 --- a/JS/index.js +++ b/JS/index.js @@ -14,22 +14,27 @@ let BrowserWidth = window.innerWidth; let BrowserHeight = window.innerHeight; let ArrowsButton = document.getElementsByClassName("Arrow"); + let SettingButton = document.getElementsByClassName("Setting")[0]; let MailButton = document.getElementsByClassName("Mail")[0]; let PostingButton = document.getElementsByClassName("Posting")[0]; +let FeedButton = document.getElementsByClassName("Feed")[0]; // Sounds const ButtonSound = new Audio("Audio/button-305770.mp3"); const WarningClick = new Audio("Audio/button-pressed-38129.mp3"); const BackgroundMusic = new Audio("Audio/soft-piano-music-312509.mp3"); +// Discoverability +let Discover = false; + // Update a timer function UpdateTime() { let TimeNow = new Date(); let Hour = TimeNow.getHours(); let Minute = TimeNow.getMinutes(); - var WebsiteTime = document.getElementsByClassName("Time")[0] + let WebsiteTime = document.getElementsByClassName("Time")[0] WebsiteTime.innerHTML = ""; if (Hour < 10) { WebsiteTime.innerHTML = WebsiteTime.innerHTML + "0" + Hour; @@ -149,12 +154,20 @@ async function PosterContainerUpdate(Direction) { // Mastodon gaining of timeline. if (localStorage.getItem(Variables.MastodonWebsite) == null) { - // The default website is Wetdry :3 console.log("No Mastodon instance. multiplying mastodon posts by 2..."); Lim = Lim * 2; Mastodon = false; } else if (MastodonLoadedFeed == 0) { - MastodonLoadedFeed = await MastodonAPI.GetTimeline(""); + // If discover is turned on... + if (Discover == true) { + if (localStorage.getItem("Remote") != null) { + MastodonLoadedFeed = await MastodonAPI.GetPublicTimeline(true, true, localStorage.getItem(Variables.MastodonWebsite), ""); + } else { + MastodonLoadedFeed = await MastodonAPI.GetPublicTimeline(true, false, localStorage.getItem(Variables.MastodonWebsite), ""); + } + } else { + MastodonLoadedFeed = await MastodonAPI.GetTimeline(""); + } } // Bluesky gaining of timeline. if (localStorage.getItem(Variables.BlueskyPDS) == null) { @@ -162,7 +175,11 @@ async function PosterContainerUpdate(Direction) { Lim = Lim * 2; Bluesky = false; } else if (BlueskyLoadedFeed.length == 0) { - BlueskyLoadedFeed = await BlueskyAPI.GetTimeline("").then((response) => response.feed); + if (Discover == true) { + BlueskyLoadedFeed = await BlueskyAPI.GetPublicTimeline("").then((response) => response.feed); + } else { + BlueskyLoadedFeed = await BlueskyAPI.GetTimeline("").then((response) => response.feed); + } } if (Direction == "Forward") { CurrentThing = CurrentThing + Lim; @@ -192,7 +209,16 @@ async function PosterContainerUpdate(Direction) { WebsiteAPIType.push("Mastodon"); i.getElementsByClassName("PostContent")[0].innerHTML = ""; if (MastodonLoadedFeed[CurrentThing + counter] == undefined) { - let TempFeed = await MastodonAPI.GetTimeline(MastodonLoadedFeed[CurrentThing + counter - 1].id); + let TempFeed; + if (Discover == true) { + if (localStorage.getItem("Remote") != null) { + TempFeed = await MastodonAPI.GetPublicTimeline(true, true, localStorage.getItem(Variables.MastodonWebsite), MastodonLoadedFeed[CurrentThing + counter - 1].id); + } else { + TempFeed = await MastodonAPI.GetPublicTimeline(true, false, localStorage.getItem(Variables.MastodonWebsite), MastodonLoadedFeed[CurrentThing + counter - 1].id); + } + } else { + TempFeed = await MastodonAPI.GetTimeline(MastodonLoadedFeed[CurrentThing + counter - 1].id); + } MastodonLoadedFeed = MastodonLoadedFeed.concat(TempFeed); } // Check for images. Reblog roundabout fix included. @@ -238,7 +264,12 @@ async function PosterContainerUpdate(Direction) { WebsiteAPIType.push("Bluesky"); i.getElementsByClassName("PostContent")[0].innerHTML = ""; if (BlueskyLoadedFeed[CurrentThing + counter] == undefined) { - let TempFeed = await BlueskyAPI.GetTimeline(BlueskyLoadedFeed[CurrentThing + counter - 1].post.indexedAt).then((response) => response.feed) + let TempFeed; + if (Discover == true) { + BlueskyLoadedFeed = await BlueskyAPI.GetPublicTimeline(BlueskyLoadedFeed[CurrentThing + counter - 1].post.indexedAt).then((response) => response.feed); + } else { + TempFeed = await BlueskyAPI.GetTimeline(BlueskyLoadedFeed[CurrentThing + counter - 1].post.indexedAt).then((response) => response.feed); + } BlueskyLoadedFeed = BlueskyLoadedFeed.concat(TempFeed); } // check for "already seen" posts, then put it as a seen post. @@ -273,7 +304,7 @@ async function PosterContainerUpdate(Direction) { i.getElementsByClassName("Username")[0].innerHTML = BlueskyLoadedFeed[CurrentThing + counter].post.author.handle; } // Apply correct facets. - var TempText = await BlueskyAPI.ApplyFacets(BlueskyLoadedFeed[CurrentThing + counter].post.record, BlueskyLoadedFeed[CurrentThing + counter].post.record.text); + let TempText = await BlueskyAPI.ApplyFacets(BlueskyLoadedFeed[CurrentThing + counter].post.record, BlueskyLoadedFeed[CurrentThing + counter].post.record.text); TempText = TempText.replace(/\r?\n|\r/g, "
"); i.getElementsByClassName("PostContent")[0].innerHTML += TempText; break; @@ -316,3 +347,12 @@ MailButton.onclick = (event) => { PostingButton.onclick = (event) => { window.location.href = "./HTML/post.html"; } + +FeedButton.onclick = (event) => { + Discover = !(Discover); + MastodonLoadedFeed = []; + BlueskyLoadedFeed = []; + CurrentThing = 0; + PosterContainerUpdate(); + +} diff --git a/JS/setting.js b/JS/setting.js index 9b98a20..045b1a5 100644 --- a/JS/setting.js +++ b/JS/setting.js @@ -5,7 +5,6 @@ import * as YoutubeAPI from "./YoutubeAPI.js"; import * as Variables from "./Variables.js"; // Settings buttons -let LocalButton = document.getElementsByClassName("Local")[0]; let RemoteButton = document.getElementsByClassName("Remote")[0]; // Website Stuff @@ -25,16 +24,7 @@ let YoutubeLogoutButton = document.getElementsByClassName("Logout Youtube")[0]; // original link let Origin = location.origin + "/HTML/setting.html" -// Change weather the timelines are public or remote -LocalButton.onclick = (event) => { - // Toggle the Variable - if (localStorage.getItem("Local") != null) { - localStorage.removeItem("Local"); - } else { - localStorage.setItem("Local", "true"); - } -} - +// Allow the user to change if Remote is a thing or not. RemoteButton.onclick = (event) => { // Toggle the Variable if (localStorage.getItem("Remote") != null) { diff --git a/index.html b/index.html index a17da89..993d7f1 100644 --- a/index.html +++ b/index.html @@ -301,10 +301,10 @@