MoreWork Merge (#40)

- Animation improvements on `Index.html`.
- Reduced the amount of code (and improved readability) of the APIs.
- Optimizations as well, but not that much.
- Accounts are now viewable. You can also follow and block them to your heart's content (I don't know what happens when you both block and follow).
- Many, many bug fixes that made it through.

Reviewed-on: #40
This commit is contained in:
CatAClock 2025-05-26 02:37:03 +00:00
parent c022194589
commit f03e73132a
19 changed files with 626 additions and 336 deletions

36
CSS/account.css Normal file
View file

@ -0,0 +1,36 @@
.Button {
margin-top: 30px;
border-style: solid;
border-width: 1px;
width: 100px;
height: 100px;
}
body {
margin: 0px;
text-align: center;
}
footer {
display: flex;
justify-content: center;
height: 5vh;
}
/* Mastodon things */
.Entry {
border-left: 2px solid;
border-right: 1px dashed;
border-top: 2px solid;
border-bottom: 2px solid;
}
.Exitry {
border-left: 1px dashed;
border-right: 2px solid;
border-top: 2px solid;
border-bottom: 2px solid;
}

View file

@ -8,6 +8,11 @@ img, video {
width: 45%;
}
body {
margin: 0px;
text-align: center;
}
.Embed {
border-style: solid;
border-radius: 1%;

View file

@ -26,11 +26,11 @@
}
@keyframes Next {
0% {
left: 0vw;
left: 85vw;
}
100% {
left: -85vw;
left: 0vw;
}
}
@ -44,11 +44,11 @@
}
@keyframes Back {
0% {
left: 0vw;
left: -85vw;
}
100% {
left: 85vw;
left: 0vw;
}
}
@ -78,24 +78,28 @@ html {
animation-play-state: running;
animation-fill-mode: forwards;
animation-duration: 4s;
animation-duration: 5s;
visibility: visible;
background-color: #000000;
color: #FFFFFF;
font-size: 18px;
}
@keyframes WarningFadeOut {
0% {
visibility: visible;
background-color: #000000;
color: #FFFFFF;
font-size: 18px;
}
20% {
background-color: #000000;
color: #000000;
}
80% {
opacity: 1;
}
99.99% {
visibility: visible;
font-size: 18px;
@ -107,10 +111,12 @@ html {
color: #000000;
font-size: 0px;
opacity: 0;
}
}
/* Classes for the main page */
/* What the entire page should look like */
.MainBefore, .MainAfter {
position: absolute;
top: 0px;
@ -119,6 +125,16 @@ html {
background-image: url("../Icons/IndexBackground.png");
}
.MainBlacked {
position: absolute;
top: 0px;
left: 0px;
color: white;
background-image: url("../Icons/IndexBlackBackground.png");
}
/* Animation things to get it working smoothly */
.MainBefore {
visibility: collapse;
@ -134,23 +150,21 @@ html {
position: absolute;
top: 0px;
left: 0px;
}
@keyframes MainFadeIn {
0% {
visibility: collapse;
}
visibility: collapse;
background-image: url("../Icons/IndexBackground.png");
}
@keyframes MainFadeIn {
79.99% {
visibility: collapse;
}
80% {
visibility: visible;
background-color: #000000;
opacity: 0;
}
100% {
background-color: #FFFFFF;
opacity: 1;
}
}
@ -195,6 +209,7 @@ html {
border-bottom-right-radius: 2% 50%;
background-color: #FFFFFF;
color: #000000;
width: 22%;
height: 23%;
}
@ -234,13 +249,11 @@ html {
font-size: 4ch;
}
.MainFooter {
.MainFooter, .MainBlackedFooter {
display: flex;
text-align: center;
justify-content: center;
background: linear-gradient(#dcdcdc, #b4b4b4);
margin-top: 75vh;
border-top-style: solid;
@ -249,3 +262,11 @@ html {
height: 20vh;
}
.MainFooter {
background: linear-gradient(#dcdcdc, #b4b4b4);
}
.MainBlackedFooter {
background: linear-gradient(#646464, #3c3c3c);
}

View file

@ -4,6 +4,10 @@ header, footer {
z-index: 1;
}
header {
height: 5vh;
}
/* Singletons */
html {
background: linear-gradient(#b4b4b4, #dcdcdc, #b4b4b4);
@ -11,8 +15,9 @@ html {
overflow-y: hidden;
}
header {
height: 5vh;
body {
margin: 0px;
text-align: center;
}
section {

View file

@ -7,6 +7,11 @@ header {
height: 5vh;
}
body {
margin: 0px;
text-align: center;
}
section {
height: 75vh;
}

View file

@ -11,18 +11,27 @@ header {
height: 5vh;
}
body {
margin: 0px;
text-align: center;
}
.Hidden {
visibility: hidden;
display: none;
}
.Button {
.Button {
margin: auto;
border-style: solid;
border-width: 6px;
border-color: #00FFFF;
padding: 25vh 8vw;
padding: 8%;
display: flex;
justify-content: center;
background: linear-gradient(#dcdcdc, #b4b4b4);
color: black;
}

34
HTML/account.html Normal file
View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>The Fediverse</title>
<meta name="description" content="Change the fucking channel already!">
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1" />
<link rel="icon" href="../Icons/favicon.ico" />
<link rel="stylesheet" href="../CSS/account.css">
<script type="module" src="../JS/account.js"></script>
<!-- Dependenci -->
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
</head>
<body style="margin: 0px; text-align: center;">
<header>
<h1>Account</h1>
</header>
<div>
<h2 class="Account"></h2>
<img class="AccountImage" />
<p class="AccountDescription"></p>
<div style="display: flex; justify-content: center;">
<p class="Button Follow">Follow!</p>
<p class="Button Block">Block!</p>
</div>
<div class="Posts">
<p>Not implemented. Posts should populate here.</p>
</div>
</div>
<footer>
<p class="Button" onclick="history.back();"><b>Back</b></p>
</footer>
</body>
</html>

View file

@ -11,7 +11,7 @@
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
</head>
<body style="margin: 0px; text-align: center;">
<body>
<!-- Grandparent is a reply post above the reply post above the regular, clicked post. -->
<header>
<h1 class="Handle GrandParent"></h1>

View file

@ -11,7 +11,7 @@
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
</head>
<body style="margin: 0px; text-align: center;">
<body>
<header>
<h1>Mail</h1>
</header>

View file

@ -11,7 +11,7 @@
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
</head>
<body style="margin: 0px; text-align: center;">
<body>
<header>
<h1>Post</h1>
</header>

View file

@ -11,15 +11,15 @@
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
</head>
<body style="margin: 0px; text-align: center;">
<body>
<header>
<h1>Setting</h1>
</header>
<div style="display: flex; justify-content: center; height: 75vh;">
<div class="Button" style="margin: auto;">
<div class="Button">
<p class="Remote">Toggle Remote</p>
</div>
<div class="Button" style="display: flex; justify-content: center; margin: auto;">
<div class="Button">
<!-- Sorry people! -->
<!-- Mastodon things. -->
<div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

View file

@ -1,23 +1,22 @@
import * as Variables from "./Variables.js";
let Token = localStorage.getItem(Variables.BlueskyAccessToken);
if (Token == null) {
console.error("No Bluesky access token!");
}
// Getters
// This gets the timeline. The cursor is a time in Z form.
export async function GetTimeline(Cursor) {
if (localStorage.getItem(Variables.BlueskyAccessToken) == null) {
console.log("No access token!");
if (Token == null) {
return "";
}
let DPoP;
let request;
if (Cursor == "") {
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", 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 FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getTimeline";
if (Cursor != "") {
FetchThing += "?cursor=" + Cursor;
}
let DPoP = await ClientDPoPPDS("GET", FetchThing);
let request = fetch(FetchThing, {method: "GET", headers: {"Authorization": "DPoP " + Token, "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"));
@ -30,20 +29,15 @@ export async function GetTimeline(Cursor) {
// 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!");
if (Token == null) {
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 FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot";
if (Cursor != "") {
FetchThing += "&cursor=" + Cursor;
}
let DPoP = await ClientDPoPPDS("GET", FetchThing);
let request = fetch(FetchThing, {method: "GET", headers: {"Authorization": "DPoP " + Token, "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"));
@ -54,15 +48,31 @@ export async function GetPublicTimeline(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) {
console.log("No access token!");
export async function GetProfile(DID) {
if (Token == null) {
return "";
}
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 FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.actor.getProfile?actor=" + DID;
let DPoP = await ClientDPoPPDS("GET", FetchThing);
let request = fetch(FetchThing, { method: "GET", headers: {"Authorization": "DPoP " + Token, "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 GetProfile(DID);
}
return body;
}
// This gets the post. If there are multiple URIs, they must be within an array.
export async function GetPosts(URIs) {
if (Token == null) {
return "";
}
let FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/app.bsky.feed.getPosts?uris=" + URIs;
let DPoP = await ClientDPoPPDS("GET", FetchThing);
let request = fetch(FetchThing, { method: "GET", headers: {"Authorization": "DPoP " + Token, "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"));
@ -82,13 +92,17 @@ export async function GetBlob(DID, CID) {
// Gets a record. The repo is the account DID, the collection is the type of record, and the key is the little bit at the end.
export async function GetRecord(Repo, Collection, RKey) {
if (Token == null) {
return "";
}
let RequestBody = {
"repo": Repo,
"collection": Collection,
"rkey": RKey
}
let DPoP = await ClientDPoPPDS("GET", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.getRecord?repo=" + Repo + "&collection=" + Collection + "&rkey=" + RKey);
let request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.getRecord?repo=" + Repo + "&collection=" + Collection + "&rkey=" + RKey, { method: "GET", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}});
let FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.getRecord?repo=" + Repo + "&collection=" + Collection + "&rkey=" + RKey;
let DPoP = await ClientDPoPPDS("GET", FetchThing);
let request = fetch(FetchThing, { method: "GET", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + Token, "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"));
@ -102,28 +116,20 @@ export async function GetRecord(Repo, Collection, RKey) {
// Creators
// This creates a post. Requires the DID of the account and the Text for the record.
export async function CreatePost(DID, Text, ContentWarning = undefined) {
if (localStorage.getItem(Variables.BlueskyAccessToken) == null) {
console.log("No access token!");
if (Token == null) {
return "";
}
let Record;
if (ContentWarning == undefined) {
Record = {
let Record = {
"$type": "app.bsky.feed.post",
"text": Text,
"createdAt": new Date(Date.now()).toISOString()
}
} else {
Record = {
"$type": "app.bsky.feed.post",
"text": Text,
"createdAt": new Date(Date.now()).toISOString(),
"labels": {
};
if (ContentWarning != undefined) {
Record.labels = {
"$type": "com.atproto.label.defs#selfLabels",
"values": [{
"val": ContentWarning
}]
}
}]
}
}
let body = await CreateRecord(DID, "app.bsky.feed.post", Record, undefined);
@ -138,48 +144,30 @@ export async function CreatePost(DID, Text, ContentWarning = undefined) {
// Creates a reply post. The RootID is always the first post, the ReplyID is the post you are replying to.
export async function CreateReplyPost(DID, Text, ReplyID, RootID, ContentWarning = undefined) {
if (localStorage.getItem(Variables.BlueskyAccessToken) == null) {
console.log("No access token!");
if (Token == null) {
return "";
}
let Record;
if (ContentWarning == undefined) {
Record = {
"$type": "app.bsky.feed.post",
"text": Text,
"createdAt": new Date(Date.now()).toISOString(),
"reply": {
"parent": {
"uri": ReplyID.uri,
"cid": ReplyID.cid
},
"root": {
"uri": RootID.uri,
"cid": RootID.cid
}
let Record = {
"$type": "app.bsky.feed.post",
"text": Text,
"createdAt": new Date(Date.now()).toISOString(),
"reply": {
"parent": {
"uri": ReplyID.uri,
"cid": ReplyID.cid
},
"root": {
"uri": RootID.uri,
"cid": RootID.cid
}
}
} else {
Record = {
"$type": "app.bsky.feed.post",
"text": Text,
"createdAt": new Date(Date.now()).toISOString(),
"reply": {
"parent": {
"uri": ReplyID.uri,
"cid": ReplyID.cid
},
"root": {
"uri": RootID.uri,
"cid": RootID.cid
},
},
"labels": {
};
if (ContentWarning != undefined) {
Record.labels = {
"$type": "com.atproto.label.defs#selfLabels",
"values": [{
"val": ContentWarning
}]
}
}]
}
}
let body = await CreateRecord(DID, "app.bsky.feed.post", Record, undefined);
@ -194,8 +182,7 @@ export async function CreateReplyPost(DID, Text, ReplyID, RootID, ContentWarning
// Creates a Thread Gate for who can reply. Requires the account DID, a post, and a list of who is allowed.
export async function CreateThreadGate(DID, Post, VisibilitySettings) {
if (localStorage.getItem(Variables.BlueskyAccessToken) == null) {
console.log("No access token!");
if (Token == null) {
return "";
}
let Record = {
@ -210,8 +197,7 @@ export async function CreateThreadGate(DID, Post, VisibilitySettings) {
// Create a like and send it to the server.
export async function CreateLike(DID, RefURI, RefCID) {
if (localStorage.getItem(Variables.BlueskyAccessToken) == null) {
console.log("No access token!");
if (Token == null) {
return "";
}
let StrongRef = {
@ -234,6 +220,44 @@ export async function CreateLike(DID, RefURI, RefCID) {
return body;
}
export async function CreateFollow(DID, SubjectDID) {
if (Token == null) {
return "";
}
let Record = {
"$type": "app.bsky.graph.follow",
"subject": SubjectDID,
"createdAt": new Date(Date.now()).toISOString()
}
let body = await GetRecord(DID, "app.bsky.graph.follow", SubjectDID);
// We might have a record. If we do, delete the thing. If we don't, create the thing.
if (body.hasOwnProperty("error") && body.error == "RecordNotFound") {
body = await CreateRecord(DID, "app.bsky.graph.follow", Record, SubjectDID);
} else {
body = await DeleteRecord(DID, "app.bsky.graph.follow", SubjectDID);
}
return body;
}
export async function CreateBlock(DID, SubjectDID) {
if (Token == null) {
return "";
}
let Record = {
"$type": "app.bsky.graph.block",
"subject": SubjectDID,
"createdAt": new Date(Date.now()).toISOString()
}
let body = await GetRecord(DID, "app.bsky.graph.block", SubjectDID);
// We might have a record. If we do, delete the thing. If we don't, create the thing.
if (body.hasOwnProperty("error") && body.error == "RecordNotFound") {
body = await CreateRecord(DID, "app.bsky.graph.block", Record, SubjectDID);
} else {
body = await DeleteRecord(DID, "app.bsky.graph.block", SubjectDID);
}
return body;
}
// Create a repost and send it to the server.
export async function CreateRepost(DID, RefURI, RefCID) {
if (localStorage.getItem(Variables.BlueskyAccessToken) == null) {
@ -262,14 +286,18 @@ export async function CreateRepost(DID, RefURI, RefCID) {
// Put record updates a record.
export async function PutRecord(Repo, Collection, Record, RKey) {
if (Token == null) {
return "";
}
let RequestBody = {
"repo": Repo,
"collection": Collection,
"record": Record,
"rkey": RKey
}
let DPoP = await ClientDPoPPDS("POST", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.putRecord");
let request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.putRecord", { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}});
let FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.putRecord";
let DPoP = await ClientDPoPPDS("POST", FetchThing);
let request = fetch(FetchThing, { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + Token, "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"));
@ -282,6 +310,9 @@ export async function PutRecord(Repo, Collection, Record, RKey) {
// Creates a record. Universal way of making things.
export async function CreateRecord(Repo, Collection, Record, RKey) {
if (Token == null) {
return "";
}
let RequestBody = {
"repo": Repo,
"collection": Collection,
@ -290,8 +321,9 @@ export async function CreateRecord(Repo, Collection, Record, RKey) {
if (RKey != undefined) {
RequestBody.rkey = RKey;
}
let DPoP = await ClientDPoPPDS("POST", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.createRecord");
let request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/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 FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.createRecord";
let DPoP = await ClientDPoPPDS("POST", FetchThing);
let request = fetch(FetchThing, { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + Token, "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"));
@ -303,6 +335,7 @@ export async function CreateRecord(Repo, Collection, Record, RKey) {
}
// Applyers
// Currently this only applies to the link Facets.
export function ApplyFacets(record, text) {
let StringArray = [];
let SplitAreas = [0];
@ -338,19 +371,24 @@ export function ApplyFacets(record, text) {
}
if (TempText == "") {
return text;
}return TempText;
}
return TempText;
}
// Deleters
// Removes a record. Can be a like, a repost, a post, etc.
export async function DeleteRecord(Repo, Collection, RKey) {
if (Token == null) {
return "";
}
let RequestBody = {
"repo": Repo,
"collection": Collection,
"rkey": RKey
}
let DPoP = await ClientDPoPPDS("POST", localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.deleteRecord");
let request = fetch(localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.deleteRecord", { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + localStorage.getItem(Variables.BlueskyAccessToken), "DPoP": DPoP}});
let FetchThing = localStorage.getItem(Variables.BlueskyPDS) + "/xrpc/com.atproto.repo.deleteRecord";
let DPoP = await ClientDPoPPDS("POST", FetchThing);
let request = fetch(FetchThing, { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + Token, "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"));

View file

@ -2,130 +2,124 @@ import * as Variables from "./Variables.js";
export const Scopes = "read write follow push";
let TokenType = localStorage.getItem(Variables.MastodonTokenType);
let Token = localStorage.getItem(Variables.MastodonAccessToken);
if (Token == null || TokenType == null) {
console.error("No Mastodon access token!");
}
// Getters
// Gets the public timeline.
export async function GetPublicTimeline(Local = false, Remote = false, Website, Cursor) {
// Variables can be found in `setting.js`
let Timeline;
if (Cursor == "") {
if (Remote == true && Local == false) {
Timeline = await fetch(Website + "/api/v1/timelines/public?remote=true")
.then((response) => response.json());
Timeline = fetch(Website + "/api/v1/timelines/public?remote=true");
} else if (Local == true && Remote == false) {
Timeline = await fetch(Website + "/api/v1/timelines/public?local=true")
.then((response) => response.json());
Timeline = fetch(Website + "/api/v1/timelines/public?local=true");
} else {
Timeline = await fetch(Website + "/api/v1/timelines/public")
.then((response) => response.json());
Timeline = fetch(Website + "/api/v1/timelines/public");
}
} else {
if (Remote == true && Local == false) {
Timeline = await fetch(Website + "/api/v1/timelines/public?remote=true&max_id=" + Cursor)
.then((response) => response.json());
Timeline = fetch(Website + "/api/v1/timelines/public?remote=true&max_id=" + Cursor);
} else if (Local == true && Remote == false) {
Timeline = await fetch(Website + "/api/v1/timelines/public?local=true&max_id=" + Cursor)
.then((response) => response.json());
Timeline = fetch(Website + "/api/v1/timelines/public?local=true&max_id=" + Cursor);
} else {
Timeline = await fetch(Website + "/api/v1/timelines/public?max_id=" + Cursor)
.then((response) => response.json());
Timeline = fetch(Website + "/api/v1/timelines/public?max_id=" + Cursor);
}
}
return Timeline;
let request = await Timeline.then((response) => response.json());
return request;
}
// Get the personal timeline.
export async function GetTimeline(Cursor) {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
export async function GetTimeline(Cursor = "") {
if (Token == null || TokenType == null) {
return "";
}
let Website = localStorage.getItem(Variables.MastodonWebsite);
// Get the varaibles that are stored in localStorage.
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?max_id=" + Cursor, {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}})
.then((response) => response.json());
// If there is a timestamp, GET with the timestamp.
let request = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/timelines/home";
if (Cursor != "") {
request += "?max_id=" + Cursor;
}
return await fetch(request, {method: "GET", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
// Get the relationship. The token houses the person you are getting a relationship for.
export async function GetRelationship(ID) {
if (Token == null || TokenType == null) {
return "";
}
return await fetch(localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/accounts/relationships?id[]=" + ID, {method: "GET", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
// Gets the favorites of a user that you have access to.
export async function GetFavorites() {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
if (Token == null || TokenType == null) {
return "";
}
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)}})
return await fetch(localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/favourites", {method: "GET", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
// Gets the bookmarks of a user that you have access to.
export async function GetBookmarks() {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
if (Token == null || TokenType == null) {
return "";
}
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)}})
return await fetch(localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/bookmarks", {method: "GET", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
// Gets the notifications of a user that you have access to.
export async function GetNotifications() {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
if (Token == null || TokenType == null) {
return "";
}
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)}})
return await fetch(localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/notifications", {method: "GET", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
// A status is just a post. It gets it.
export async function GetStatus(ID) {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
if (Token == null || TokenType == null) {
return "";
}
let Website = localStorage.getItem(Variables.MastodonWebsite);
// Get the varaibles that are stored in Variables and then input it.
return await fetch(Website + "/api/v1/statuses/" + ID, {method: "GET", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}})
return await fetch(localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/statuses/" + ID, {method: "GET", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
// Creators
// Make a status
export async function CreateStatus(Text, SpoilerText = undefined, Visibility = "public") {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
if (Token == null || TokenType == null) {
return "";
}
// Stolen from StackOverflow
// Stolen from StackOverflow.
Text = Text.replace(/(?:\r|\n|\r\n)/g, '<br>');
// Check for a token
let Website = localStorage.getItem(Variables.MastodonWebsite);
let request;
if (SpoilerText == undefined) {
request = fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}});
} else {
request = fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility + "&spoiler_text=" + SpoilerText, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}});
// Get the correct fetch body.
let FetchThing = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility;
if (SpoilerText != undefined) {
FetchThing += "&spoiler_text=" + SpoilerText;
}
// Send the request.
let request = fetch(FetchThing, {method: "POST", headers: {"Authorization": TokenType + " " + Token}});
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+)/);
if (SpoilerText == undefined) {
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)}});
} else {
request = fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility + "&spoiler_text=" + SpoilerText, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}});
// Get the correct fetch body.
FetchThing = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/statuses?status=" + Text.slice(0, matches[0] - 1) + "&visibility=" + Visibility
if (SpoilerText != undefined) {
FetchThing += "&spoiler_text=" + SpoilerText;
}
// Send the request.
request = fetch(FetchThing, {method: "POST", headers: {"Authorization": TokenType + " " + Token}});
body = await request.then((response) => response.json());
await CreateReplyStatus(Text.slice(matches[0] - 1, SpoilerText, Text.length - 1), Visibility, body.id);
}
@ -133,30 +127,30 @@ export async function CreateStatus(Text, SpoilerText = undefined, Visibility = "
}
export async function CreateReplyStatus(Text, SpoilerText = undefined, Visibility = "public", ReplyID) {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
if (Token == null || TokenType == null) {
return "";
}
// Stolen from StackOverflow
Text = Text.replace(/(?:\r|\n|\r\n)/g, '<br>');
// Check for a token
let Website = localStorage.getItem(Variables.MastodonWebsite);
let request;
if (SpoilerText == undefined) {
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)}});
} else {
request = fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility + "&in_reply_to_id=" + ReplyID + "&spoiler_text=" + SpoilerText, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}});
// Get the correct fetch body.
let FetchThing = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility + "&in_reply_to_id=" + ReplyID;
if (SpoilerText != undefined) {
FetchThing += "&spoiler_text=" + SpoilerText;
}
// Send the request.
let request = fetch(FetchThing, {method: "POST", headers: {"Authorization": TokenType + " " + Token}});
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+)/);
if (SpoilerText == undefined) {
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)}});
} else {
request = fetch(Website + "/api/v1/statuses?status=" + Text + "&visibility=" + Visibility + "&in_reply_to_id=" + ReplyID + "&spoiler_text=" + SpoilerText, {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}});
// Get the correct fetch body.
FetchThing = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/statuses?status=" + Text.slice(0, matches[0] - 1) + "&visibility=" + Visibility + "&in_reply_to_id=" + ReplyID;
if (SpoilerText != undefined) {
FetchThing += "&spoiler_text=" + SpoilerText;
}
// Send the request.
request = fetch(FetchThing, {method: "POST", headers: {"Authorization": TokenType + " " + Token}});
body = await request.then((response) => response.json());
await CreateReplyStatus(Text.slice(matches[0] - 1, Text.length - 1), SpoilerText, Visibility, body.id);
}
@ -164,33 +158,60 @@ export async function CreateReplyStatus(Text, SpoilerText = undefined, Visibilit
}
export async function CreateFavorite(ID, IsFavorited) {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
if (Token == null || TokenType == null) {
return "";
}
let Website = localStorage.getItem(Variables.MastodonWebsite);
let request = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/statuses/" + ID;
if (IsFavorited == false) {
return await fetch(Website + "/api/v1/statuses/" + ID + "/favourite", {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}})
.then((response) => response.json());
request += "/favourite";
} else {
return await fetch(Website + "/api/v1/statuses/" + ID + "/unfavourite", {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}})
.then((response) => response.json());
request += "/unfavourite";
}
return await fetch(request, {method: "POST", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
export async function CreateReblog(ID, IsReblogged) {
if (Token == null || TokenType == null) {
return "";
}
let request = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/statuses/" + ID;
if (IsReblogged == false) {
request += "/reblog";
} else {
request += "/unreblog";
}
return await fetch(request, {method: "POST", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
export async function CreateFollow(ID, IsFollowed) {
if (localStorage.getItem(Variables.MastodonAccessToken) == null) {
console.log("No access token!");
return "";
}
let Website = localStorage.getItem(Variables.MastodonWebsite);
if (IsReblogged == false) {
return await fetch(Website + "/api/v1/statuses/" + ID + "/reblog", {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}})
.then((response) => response.json());
let request = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/accounts/" + ID;
if (IsFollowed == false) {
request += "/follow";
} else {
return await fetch(Website + "/api/v1/statuses/" + ID + "/unreblog", {method: "POST", headers: {"Authorization": localStorage.getItem(Variables.MastodonTokenType) + " " + localStorage.getItem(Variables.MastodonAccessToken)}})
.then((response) => response.json());
request += "/unfollow";
}
return await fetch(request, {method: "POST", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
export async function CreateBlock(ID, IsBlocked) {
if (Token == null || TokenType == null) {
return "";
}
let request = localStorage.getItem(Variables.MastodonWebsite) + "/api/v1/accounts/" + ID;
if (IsReblogged == false) {
request += "/block";
} else {
request += "/unblock";
}
return await fetch(request, {method: "POST", headers: {"Authorization": TokenType + " " + Token}})
.then((response) => response.json());
}
// Things to make this work

112
JS/account.js Normal file
View file

@ -0,0 +1,112 @@
import * as MastodonAPI from "./MastodonAPI.js";
import * as BlueskyAPI from "./BlueskyAPI.js";
import * as TumblrAPI from "./TumblrAPI.js";
import * as Variables from "./Variables.js";
let website = document.location.href.split("website=")[1];
let post = JSON.parse(localStorage.getItem("post"));
// HTML elements.
let AccountName = document.getElementsByClassName("Account")[0];
let AccountImage = document.getElementsByClassName("AccountImage")[0];
let AccountDescription = document.getElementsByClassName("AccountDescription")[0];
let FollowButton = document.getElementsByClassName("Follow")[0];
let BlockButton = document.getElementsByClassName("Block")[0];
// Other vars.
let Following = false;
let Blocking = false;
GetAccount();
FollowButton.onclick = (event) => {
FollowBoop();
}
async function FollowBoop() {
if (website == "Mastodon") {
let Relations = await MastodonAPI.GetRelationship(post.account.id);
await MastodonAPI.CreateFollow(post.account.id, Relations[0].following);
} else if (website == "Bluesky") {
await BlueskyAPI.CreateFollow(localStorage.getItem(Variables.BlueskyDID), post.post.author.did);
}
SetFollow();
}
function SetFollow() {
Following = !(Following);
if (Following == true) {
FollowButton.innerHTML = "Unfollow...";
} else {
FollowButton.innerHTML = "Follow!";
}
}
BlockButton.onclick = (event) => {
BlockBoop();
}
async function BlockBoop() {
if (website == "Mastodon") {
let Relations = await MastodonAPI.GetRelationship(post.account.id);
await MastodonAPI.CreateBlock(post.account.id, Relations[0].blocking);
} else if (website == "Bluesky") {
await BlueskyAPI.CreateBlock(localStorage.getItem(Variables.BlueskyDID), post.post.author.did);
}
SetBlock();
}
function SetBlock() {
Blocking = !(Blocking);
if (Blocking == true) {
BlockButton.innerHTML = "Unblock...";
} else {
BlockButton.innerHTML = "Block!";
}
}
async function GetAccount() {
if (website == "Mastodon") {
// Get the relationshop.
let Relations = await MastodonAPI.GetRelationship(post.account.id);
// Set the buttons using the relationship.
Following = !(Relations[0].following);
Blocking = !(Relations[0].blocking);
SetFollow();
SetBlock();
// Check for a reblog.
if (post.reblog != null) {
post = post.reblog;
}
AccountName.innerHTML = post.account.username;
AccountDescription.innerHTML = post.account.note;
AccountImage.setAttribute("src", post.account.avatar);
// Build the fields
let Field = "<div style=\"display: flex; justify-content: center;\">";
for (let i of post.account.fields) {
Field += "<b class=\"Entry\">" + i.name + "</b><em class=\"Exitry\">" + i.value + "</em>";
}
Field += "</div>";
AccountDescription.innerHTML += Field;
} else if (website == "Bluesky") {
// Set the relationship follows
let thing = await BlueskyAPI.GetRecord(localStorage.getItem(Variables.BlueskyDID), "app.bsky.graph.follow", post.post.author.did);
let thing2 = await BlueskyAPI.GetRecord(localStorage.getItem(Variables.BlueskyDID), "app.bsky.graph.block", post.post.author.did);
if (thing.hasOwnProperty("error") && thing.error == "RecordNotFound") {
Following = true;
} else {
Following = false;
}
if (thing2.hasOwnProperty("error") && thing2.error == "RecordNotFound") {
Blocking = true;
} else {
Blocking = false;
}
SetFollow();
SetBlock();
let account = await BlueskyAPI.GetProfile(post.post.author.did);
AccountName.innerHTML = account.handle;
AccountDescription.innerHTML = account.description;
AccountImage.setAttribute("src", account.avatar);
}
}

View file

@ -43,88 +43,72 @@ Reply.onclick = (event) => {
window.location.href = "../../HTML/post.html?website=" + website;
}
// Seleecting other posts
// Other post stuff.
for (let i of document.getElementsByClassName("Regular")) {
i.onclick = (event) => {
if (i.classList.contains("Handle")) {
window.location.href = "../../HTML/account.html?website=" + website;
}
}
}
// Selecting other posts.
for (let i of document.getElementsByClassName("Parent")) {
i.onclick = (event) => {
SetThreadPost(Parentpost);
SetThreadPost(Parentpost, i);
}
}
for (let i of document.getElementsByClassName("GrandParent")) {
i.onclick = (event) => {
SetThreadPost(GrandParentpost);
SetThreadPost(GrandParentpost, i);
}
}
async function SetThreadPost(element) {
async function SetThreadPost(element, i) {
if (website == "Mastodon") {
localStorage.setItem("post", JSON.stringify(element));
} else if (website == "Bluesky") {
let Temp = await BlueskyAPI.GetPosts(element.uri);
element.post = Temp.posts[0];
localStorage.setItem("post", JSON.stringify(element));
}
document.location.href = "./expanded.html?website=" + website;
localStorage.setItem("post", JSON.stringify(element));
} else if (website == "Bluesky") {
let Temp = await BlueskyAPI.GetPosts(element.uri);
element.post = Temp.posts[0];
localStorage.setItem("post", JSON.stringify(element));
}
if (i.classList.contains("Handle")) {
window.location.href = "../../HTML/account.html?website=" + website;
} else {
window.location.href = "../../HTML/expanded.html?website=" + website;
}
}
// Functions and things.
async function GetPost() {
if (website == "Mastodon") {
// Check for a reblog.
if (post.reblog != null) {
document.getElementsByClassName("PostText Regular")[0].innerHTML = post.reblog.content;
document.getElementsByClassName("Handle Regular")[0].innerHTML = post.reblog.account.username + " ( R: " + post.account.username + " )";
if (post.reblog.media_attachments.length != 0) {
for (let i of post.reblog.media_attachments) {
ApplyMedia(i, document.getElementsByClassName("Images Regular")[0]);
}
}
} else {
document.getElementsByClassName("PostText Regular")[0].innerHTML = post.content;
document.getElementsByClassName("Handle Regular")[0].innerHTML = post.account.username;
// Show the image if it exists.
if (post.media_attachments.length != 0) {
for (let i of post.media_attachments) {
ApplyMedia(i, document.getElementsByClassName("Images Regular")[0]);
}
}
}
// Set the texts. It's opposite because "setting" causes it to switch.
post = await MastodonAPI.GetStatus(post.id);
FavoriteFlipper = !(post.favourited);
BoostFlipper = !(post.reblogged);
SetFavorite();
SetBoost();
// Check for a reblog.
if (post.reblog != null) {
document.getElementsByClassName("Handle Regular")[0].innerHTML = post.reblog.account.username + " ( R: " + post.account.username + " )";
post = post.reblog;
} else {
document.getElementsByClassName("Handle Regular")[0].innerHTML = post.account.username;
}
document.getElementsByClassName("PostText Regular")[0].innerHTML = post.content;
// Show the image if it exists.
if (post.media_attachments.length != 0) {
for (let i of post.media_attachments) {
ApplyMedia(i, document.getElementsByClassName("Images Regular")[0]);
}
}
// Now time to see if there are any parents
if (post.in_reply_to_id != null) {
Parentpost = await MastodonAPI.GetStatus(post.in_reply_to_id);
document.getElementsByClassName("Origin Parent")[0].innerHTML = website;
document.getElementsByClassName("Handle Parent")[0].innerHTML = Parentpost.account.username;
if (Parentpost.spoiler_text != "") {
document.getElementsByClassName("PostText Parent")[0].innerHTML = "WARNING: " + Parentpost.spoiler_text;
} else {
document.getElementsByClassName("PostText Parent")[0].innerHTML = Parentpost.content;
}
if (Parentpost.media_attachments.length != 0) {
for (let i of Parentpost.media_attachments) {
ApplyMedia(i, document.getElementsByClassName("Images Parent")[0]);
}
}
let ParentPost = await MastodonReplylFunction("Parent", post.in_reply_to_id);
// Now time to see if there are any grandparents
if (Parentpost.in_reply_to_id != null) {
GrandParentpost = await MastodonAPI.GetStatus(Parentpost.in_reply_to_id);
document.getElementsByClassName("Origin GrandParent")[0].innerHTML = website;
document.getElementsByClassName("Handle GrandParent")[0].innerHTML = GrandParentpost.account.username;
if (GrandParentpost.spoiler_text != "") {
document.getElementsByClassName("PostText GrandParent")[0].innerHTML = "WARNING: " + GrandParentpost.spoiler_text;
} else {
document.getElementsByClassName("PostText GrandParent")[0].innerHTML = GrandParentpost.content;
}
if (GrandParentpost.media_attachments.length != 0) {
for (let i of GrandParentpost.media_attachments) {
ApplyMedia(i, document.getElementsByClassName("Images GrandParent")[0]);
}
}
if (ParentPost == undefined && ParentPost.in_reply_to_id != null) {
MastodonReplylFunction("GrandParent", ParentPost.in_reply_to_id);
}
}
} else if (website == "Bluesky") {
@ -157,43 +141,10 @@ async function GetPost() {
SetBoost();
// Now time to see if there are any parents.
if (post.hasOwnProperty("reply")) {
Parentpost = await BlueskyAPI.GetRecord(post.reply.parent.uri.split("/")[2], post.reply.parent.uri.split("/")[3], post.reply.parent.uri.split("/")[4]);
document.getElementsByClassName("Origin Parent")[0].innerHTML = website;
document.getElementsByClassName("Handle Parent")[0].innerHTML = post.reply.parent.author.handle;
Text = BlueskyAPI.ApplyFacets(Parentpost.value, Parentpost.value.text);
Text = Text.replace(/\r?\n|\r/g, "<br/>");
// Sensitive topic :3
if (Parentpost.value.hasOwnProperty("labels") && Parentpost.value.labels.length != 0) {
document.getElementsByClassName("PostText Parent")[0].innerHTML = "LABELS APPLIED: ";
console.log(Parentpost.value.labels);
for (let lab of Parentpost.value.labels.values) {
document.getElementsByClassName("PostText Parent")[0].innerHTML += lab.val + " ";
}
} else {
document.getElementsByClassName("PostText Parent")[0].innerHTML = Text;
if (Parentpost.value.hasOwnProperty("embed")) {
ApplyMedia(Parentpost.value.embed, document.getElementsByClassName("Images Parent")[0], post.reply.parent.author.did);
}
}
Parentpost = await BlueskyReplyFunction("Parent", post.reply.parent, post.reply.parent.author);
// Now time to see if there are any grandparents.
if (Parentpost.value.hasOwnProperty("reply")) {
GrandParentpost = await BlueskyAPI.GetRecord(Parentpost.value.reply.parent.uri.split("/")[2], Parentpost.value.reply.parent.uri.split("/")[3], Parentpost.value.reply.parent.uri.split("/")[4]);
document.getElementsByClassName("Origin GrandParent")[0].innerHTML = website;
document.getElementsByClassName("Handle GrandParent")[0].innerHTML = post.reply.grandparentAuthor.handle;
Text = BlueskyAPI.ApplyFacets(GrandParentpost.value, GrandParentpost.value.text);
Text = Text.replace(/\r?\n|\r/g, "<br/>");
// Sensitive topic :3
if (GrandParentpost.value.hasOwnProperty("labels") && GrandParentpost.value.labels.length != 0) {
document.getElementsByClassName("PostText GrandParent")[0].innerHTML = "LABELS APPLIED: ";
for (let lab of GrandParentpost.value.labels.values) {
document.getElementsByClassName("PostText GrandParent")[0].innerHTML += lab.val + " ";
}
} else {
document.getElementsByClassName("PostText GrandParent")[0].innerHTML = Text;
if (GrandParentpost.value.hasOwnProperty("embed")) {
ApplyMedia(GrandParentpost.value.embed, document.getElementsByClassName("Images GrandParent")[0], post.reply.grandparentAuthor.did);
}
}
GrandParentpost = await BlueskyReplyFunction("Parent", Parentpost.value.reply.parent, post.reply.grandparentAuthor);
}
}
} else {
@ -201,6 +152,45 @@ async function GetPost() {
}
}
// Because of repeat code, we can put it into it's own function. Hell, more of a thread can be added later.
async function MastodonReplylFunction(ClassName, post) {
let OtherPost = await MastodonAPI.GetStatus(post);
document.getElementsByClassName("Origin " + ClassName)[0].innerHTML = website;
document.getElementsByClassName("Handle " + ClassName)[0].innerHTML = OtherPost.account.username;
if (OtherPost.spoiler_text != "") {
document.getElementsByClassName("PostText " + ClassName)[0].innerHTML = "WARNING: " + OtherPost.spoiler_text;
} else {
document.getElementsByClassName("PostText " + ClassName)[0].innerHTML = OtherPost.content;
}
if (OtherPost.media_attachments.length != 0) {
for (let i of OtherPost.media_attachments) {
ApplyMedia(i, document.getElementsByClassName("Images " + ClassName)[0]);
}
}
return OtherPost;
}
async function BlueskyReplyFunction(ClassName, post, OtherAuthor) {
let OtherPost = await BlueskyAPI.GetRecord(post.uri.split("/")[2], post.uri.split("/")[3], post.uri.split("/")[4]);
document.getElementsByClassName("Origin " + ClassName)[0].innerHTML = website;
document.getElementsByClassName("Handle " + ClassName)[0].innerHTML = OtherAuthor.handle;
Text = BlueskyAPI.ApplyFacets(OtherPost.value, OtherPost.value.text);
Text = Text.replace(/\r?\n|\r/g, "<br/>");
// Sensitive topic :3
if (OtherPost.value.hasOwnProperty("labels") && OtherPost.value.labels.length != 0) {
document.getElementsByClassName("PostText " + ClassName)[0].innerHTML = "LABELS APPLIED: ";
for (let lab of OtherPost.value.labels.values) {
document.getElementsByClassName("PostText " + ClassName)[0].innerHTML += lab.val + " ";
}
} else {
document.getElementsByClassName("PostText " + ClassName)[0].innerHTML = Text;
if (OtherPost.value.hasOwnProperty("embed")) {
ApplyMedia(OtherPost.value.embed, document.getElementsByClassName("Images " + ClassName)[0], OtherAuthor.did);
}
}
return OtherPost;
}
// Applyers. Essentially applies a thing to an operation.
// Finds any associated media and applies it to the post.
async function ApplyMedia(Media, Element, Author = undefined) {
@ -208,15 +198,15 @@ async function ApplyMedia(Media, Element, Author = undefined) {
if (website == "Mastodon") {
if (Media.type == "image") {
if (Media.remote_url == null) {
Element.innerHTML += "<img src=" + Media.url + " alt='" + EscapeRegExp(Media.description) + "'/>";
Element.innerHTML += "<img src=" + Media.url + " alt=\"" + EscapeRegExp(Media.description) + "\"/>";
} else {
Element.innerHTML += "<img src=" + Media.remote_url + " alt='" + EscapeRegExp(Media.description) + "'/>";
Element.innerHTML += "<img src=" + Media.remote_url + " alt=\"" + EscapeRegExp(Media.description) + "\"/>";
}
} else if (Media.type == "video") {
} else if (Media.type == "video" || Media.type == "gifv") {
if (Media.remote_url == null) {
Element.innerHTML += "<video controls src=" + Media.url + " alt='" + EscapeRegExp(Media.description) + "'></video>";
Element.innerHTML += "<video controls src=" + Media.url + " alt=\"" + EscapeRegExp(Media.description) + "\"></video>";
} else {
Element.innerHTML += "<video controls src=" + Media.remote_url + " alt='" + EscapeRegExp(Media.description) + "'></video>";
Element.innerHTML += "<video controls src=" + Media.remote_url + " alt=\"" + EscapeRegExp(Media.description) + "\"></video>";
}
}
} else if (website == "Bluesky") {
@ -229,7 +219,7 @@ async function ApplyMedia(Media, Element, Author = undefined) {
Element.innerHTML += "<p class='Embed'>" + Text + "</p><br/>";
// To stop confusion: this is for the RECORD EMBED! It gets an image from the record embed and puts it there.
if (Texty.posts[0].record.embed.$type == "app.bsky.embed.images" || Texty.posts[0].record.embed.$type == "app.bsky.embed.video") {
await BlueskyBlobEmbed(Texty.posts[0].record.embed, Element, Author);
await BlueskyBlobEmbed(Texty.posts[0].record.embed, Element, Texty.posts[0].author.did);
}
// Just a record on it's own.
} else if (Media.$type == "app.bsky.embed.record") {
@ -238,7 +228,7 @@ async function ApplyMedia(Media, Element, Author = undefined) {
Text = Text.replace(/\r?\n|\r/g, "<br/>");
Element.innerHTML += "<p class='Embed'>" + Text + "</p><br/>";
if (Texty.posts[0].record.embed.$type == "app.bsky.embed.images" || Texty.posts[0].record.embed.$type == "app.bsky.embed.video") {
await BlueskyBlobEmbed(Texty.posts[0].record.embed, Element, Author);
await BlueskyBlobEmbed(Texty.posts[0].record.embed, Element, Texty.posts[0].author.did);
}
// Image on it's own
} else if (Media.$type == "app.bsky.embed.images" || Media.$type == "app.bsky.embed.video") {
@ -253,7 +243,7 @@ async function BlueskyBlobEmbed(Blob, Element, Author) {
for (let i of Blob.images) {
var Blobby = await BlueskyAPI.GetBlob(Author, i.image.ref.$link);
var ObjectURL = URL.createObjectURL(Blobby);
Element.innerHTML += "<img src=" + ObjectURL + " alt='" + EscapeRegExp(i.alt) + "'/>";
Element.innerHTML += "<img src=" + ObjectURL + " alt=\"" + EscapeRegExp(i.alt) + "\"/>";
}
// Video embed.
} else if (Blob.$type == "app.bsky.embed.video") {

View file

@ -92,6 +92,7 @@ function Music() {
// Clicking the next button
ArrowsButton[1].onclick = (event) => {
if (!ContainerContainer.classList.contains("NextAnimation")) {
UpdateOtherContainers(1);
ContainerContainer.classList.add("NextAnimation");
ButtonSound.play();
setTimeout(() => {
@ -104,6 +105,7 @@ ArrowsButton[1].onclick = (event) => {
// Clicking the back button
ArrowsButton[0].onclick = (event) => {
if (!ContainerContainer.classList.contains("BackAnimation")) {
UpdateOtherContainers(3);
ContainerContainer.classList.add("BackAnimation");
ButtonSound.play();
setTimeout(() => {
@ -113,6 +115,14 @@ ArrowsButton[0].onclick = (event) => {
}
}
function UpdateOtherContainers(Num) {
let Posts = ContainerContainer.getElementsByClassName("PostContainer")[Num].children;
for (let i = 0; i < ClickableContainers.length; i++) {
Posts[i].getElementsByClassName("Username")[0].innerHTML = ClickableContainers[i].getElementsByClassName("Username")[0].innerHTML;
Posts[i].getElementsByClassName("PostContent")[0].innerHTML = ClickableContainers[i].getElementsByClassName("PostContent")[0].innerHTML;
}
}
// These numbers are here so you don't go back.
let MastodonLoadedFeed = [];
let BlueskyLoadedFeed = [];
@ -221,22 +231,19 @@ async function PosterContainerUpdate(Direction) {
}
MastodonLoadedFeed = MastodonLoadedFeed.concat(TempFeed);
}
// Check for images. Reblog roundabout fix included.
// put the reblog into the regular post; Make changes as necessary.
if (MastodonLoadedFeed[CurrentThing + counter].reblog != null) {
if (MastodonLoadedFeed[CurrentThing + counter].reblog.media_attachments.length != 0) {
if (MastodonLoadedFeed[CurrentThing + counter].reblog.media_attachments[0].type == "image") {
i.getElementsByClassName("PostContent")[0].innerHTML += "This post has an image!<br/>";
} else if (MastodonLoadedFeed[CurrentThing + counter].reblog.media_attachments[0].type == "video") {
i.getElementsByClassName("PostContent")[0].innerHTML += "This post has a video!<br/>";
}
}
i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].reblog.account.username + " ( R: " + MastodonLoadedFeed[CurrentThing + counter].account.username + " )";
MastodonLoadedFeed[CurrentThing + counter] = MastodonLoadedFeed[CurrentThing + counter].reblog;
} else {
if (MastodonLoadedFeed[CurrentThing + counter].media_attachments.length != 0) {
if (MastodonLoadedFeed[CurrentThing + counter].media_attachments[0].type == "image") {
i.getElementsByClassName("PostContent")[0].innerHTML += "This post has an image!<br/>";
} else if (MastodonLoadedFeed[CurrentThing + counter].media_attachments[0].type == "video") {
i.getElementsByClassName("PostContent")[0].innerHTML += "This post has a video!<br/>";
}
i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username
}
// Check for images.
if (MastodonLoadedFeed[CurrentThing + counter].media_attachments.length != 0) {
if (MastodonLoadedFeed[CurrentThing + counter].media_attachments[0].type == "image") {
i.getElementsByClassName("PostContent")[0].innerHTML += "This post has an image!<br/>";
} else if (MastodonLoadedFeed[CurrentThing + counter].media_attachments[0].type == "video" || MastodonLoadedFeed[CurrentThing + counter].media_attachments[0].type == "gifv") {
i.getElementsByClassName("PostContent")[0].innerHTML += "This post has a video!<br/>";
}
}
// Check for a thread.
@ -249,14 +256,7 @@ async function PosterContainerUpdate(Direction) {
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 + " )";
} else {
i.getElementsByClassName("PostContent")[0].innerHTML += MastodonLoadedFeed[CurrentThing + counter].content;
i.getElementsByClassName("Username")[0].innerHTML = MastodonLoadedFeed[CurrentThing + counter].account.username;
}
i.getElementsByClassName("PostContent")[0].innerHTML += MastodonLoadedFeed[CurrentThing + counter].content;
break;
// Bsky
case 1:
@ -326,14 +326,14 @@ 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.")
console.log("Culled a duplicate post.");
Timeline = CheckForDups(Timeline, DupeArray, counter);
}
}
return Timeline;
}
// Open the settings
// Open the settings.
SettingButton.onclick = (event) => {
window.location.href = "./HTML/setting.html";
}
@ -343,16 +343,29 @@ MailButton.onclick = (event) => {
window.location.href = "./HTML/mail.html";
}
// Open the posting area
// Open the posting area.
PostingButton.onclick = (event) => {
window.location.href = "./HTML/post.html";
}
// Change the feed to a "public" feed.
FeedButton.onclick = (event) => {
if (Discover == false) {
let Footer = document.getElementsByClassName("MainFooter")[0];
Main.classList.add("MainBlacked");
Main.classList.remove("MainAfter");
Footer.classList.add("MainBlackedFooter");
Footer.classList.remove("MainFooter");
} else {
let Footer = document.getElementsByClassName("MainBlackedFooter")[0];
Main.classList.add("MainAfter");
Main.classList.remove("MainBlacked");
Footer.classList.add("MainFooter");
Footer.classList.remove("MainBlackedFooter");
}
Discover = !(Discover);
MastodonLoadedFeed = [];
BlueskyLoadedFeed = [];
CurrentThing = 0;
PosterContainerUpdate();
}

View file

@ -52,9 +52,9 @@ async function Post() {
} else {
await MastodonAPI.CreateReplyStatus(Text, WarningInputArea.value, TempVisible, JSON.parse(localStorage.getItem("post")).id);
}
return;
InputArea.value = "";
WarningInputArea.value = "";
return;
} else if (website == "All") {
if (WarningInputArea.value == "") {
await MastodonAPI.CreateStatus(Text, undefined, TempVisible);
@ -99,6 +99,7 @@ async function Post() {
}
InputArea.value = "";
WarningInputArea.value = "";
return;
} else if (website == "All") {
let Post;
if (WarningInputArea.value == "") {