Skip to content

Commit

Permalink
fetch timeout + const linting + various polish
Browse files Browse the repository at this point in the history
Co-authored-by: Varun Singh <[email protected]>
Co-authored-by: Birdy <[email protected]>
  • Loading branch information
3 people committed Dec 13, 2023
1 parent da6bba7 commit 35a4a7f
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 52 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
"no-var": [
"error"
],
"prefer-const": [
"error"
],
"@stylistic/arrow-spacing": [
"error",
{
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h1>Blend</h1>
<button class="page-button" id="prev-page" type="button">
<img src="media/previous-page.svg" class="icon-small" alt="previous page button icon">
</button>
<span class="page-label" id="page-label">Page 2</span>
<span class="page-label" id="page-label"></span>
<button class="page-button" id="next-page" type="button">
<img src="media/next-page.svg" class="icon-small" alt="next page button icon">
</button>
Expand Down
2 changes: 1 addition & 1 deletion settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ <h2>Lemmy</h2>
</div>
</section>
</main>
<script type="module" src="./src/scripts/settings-page.js"></script>
<script type="module" src="./src/scripts/settingsPage.js"></script>
</body>
</html>

Expand Down
2 changes: 1 addition & 1 deletion src/scripts/entity/InstanceEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class InstanceEntry extends HTMLElement {
</button>
</li>
`;
let script = document.createElement("script");
const script = document.createElement("script");
script.type = "module";
script.textContent = `
import { handleRemoveInstance } from './src/scripts/instanceList.js';
Expand Down
10 changes: 8 additions & 2 deletions src/scripts/fetchers/LemmyFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ export class LemmyFetcher extends Fetcher {
const posts = [];
try {
const url = instanceUrl + API_V3 + "&limit=" + NUM_POSTS;
const response = await fetch(url);

const timeoutDelay = 10000;
const response = await Promise.race([
fetch(url),
new Promise((_resolve, reject) => setTimeout(() => reject("Fetch calls timed out."), timeoutDelay)),
]);

if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
let response_data = await response.json();
const response_data = await response.json();
const posts_json = response_data.posts;
posts_json.forEach((post) => {
posts.push(post);
Expand Down
7 changes: 6 additions & 1 deletion src/scripts/fetchers/MastodonFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ export class MastodonFetcher extends Fetcher {
try {
const hashtags = await this.#fetchTrendingTags(instURL + TAGS_SUFFIX);
const fetchPromises = hashtags.map(tag => this.#fetchPostsByHashtag(instURL, tag.name));
const responses = await Promise.all(fetchPromises);
const timeoutDelay = 10000;
const responses = await Promise.race(
[
Promise.all(fetchPromises),
new Promise((_resolve, reject) => setTimeout(() => reject("Fetch calls timed out."), timeoutDelay)),
]);
const posts = responses.flat();
posts.push(...posts);
return posts;
Expand Down
10 changes: 5 additions & 5 deletions src/scripts/instanceList.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function saveLists(instanceLists, storage = localStorage) {
export async function addInstance(network, url, storage = localStorage) {
if (!(ALLOWED_NETWORKS.has(network)) || !validUrl(url)) return false;

let instanceList = fetchInstanceLists(storage);
const instanceList = fetchInstanceLists(storage);
// network is already guaranteed to be in ALLOWED_NETWORKS and thus on the default list
instanceList[network].unshift(url);
storage.setItem(INST_LISTS, JSON.stringify(instanceList));
Expand All @@ -59,9 +59,9 @@ export async function addInstance(network, url, storage = localStorage) {
* @param {Storage} storage
*/
export function handleRemoveInstance(network, url, storage = localStorage) {
let i = removeInstance(network, url, storage);
const i = removeInstance(network, url, storage);
if (i !== -1) {
let list = document.getElementById(`${network}-instance-list`);
const list = document.getElementById(`${network}-instance-list`);
// first child will always be add instance input box
// index 0 of instance list will be child 1
list.removeChild(list.children[i + 1]);
Expand All @@ -75,9 +75,9 @@ export function handleRemoveInstance(network, url, storage = localStorage) {
* @returns {number} Index removed if successful, -1 otherwise
*/
export function removeInstance(network, url, storage = localStorage) {
let instanceLists = fetchInstanceLists(storage);
const instanceLists = fetchInstanceLists(storage);
if (network in instanceLists && instanceLists[network].includes(url)) {
let ind = instanceLists[network].indexOf(url);
const ind = instanceLists[network].indexOf(url);
instanceLists[network].splice(ind, 1);
saveLists(instanceLists, storage);
return ind;
Expand Down
22 changes: 13 additions & 9 deletions src/scripts/pageBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,29 @@ export async function buildPage(HANDLERS) {
nextPage.disabled = true;
prevPage.disabled = true;

let postsByNetwork = {};
const postsByNetwork = {};
let maxLengthArray = 0;
const instLists = fetchInstanceLists();
for (let [network, instanceList] of Object.entries(instLists)) {
let fetcher = new HANDLERS[network]["fetcher"]();
let postBuilder = new HANDLERS[network]["postBuilder"]();
// Iterate over networks (currently just Mastodon and Lemmy)
for (const [network, instanceList] of Object.entries(instLists)) {
// Get object handlers for API and post formatting
const fetcher = new HANDLERS[network]["fetcher"]();
const postBuilder = new HANDLERS[network]["postBuilder"]();
postsByNetwork[network] = [];
for (let url of instanceList) {
let res = await fetcher.fetchPosts(url);
for (let post of res) {
// Iterate over each network's instance list
for (const url of instanceList) {
const res = await fetcher.fetchPosts(url);
if (res === null) continue;
for (const post of res) {
postsByNetwork[network].push(postBuilder.buildPost(post));
}
}
maxLengthArray = Math.max(maxLengthArray, postsByNetwork[network].length);
}

let posts = [];
const posts = [];
for (let i = 0; i < maxLengthArray; i++) {
for (let [network] of Object.entries(instLists)) {
for (const [network] of Object.entries(instLists)) {
if (i < postsByNetwork[network].length) {
posts.push(postsByNetwork[network][i]);
}
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/postBuilder/LemmyPostBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class LemmyPostBuilder extends PostBuilder {
* @returns {Object}- A Post element created from raw json data from the API
*/
buildPost(post) {
let newPost = document.createElement("fedi-post");
const newPost = document.createElement("fedi-post");
newPost.setAttribute("id", post.post.id);
newPost.setAttribute("content", this.#extractPostContent(post.post)); // Either a url or body or both.
newPost.setAttribute("author-name", post.creator.name);
Expand Down
30 changes: 15 additions & 15 deletions src/scripts/settings-page.js → src/scripts/settingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { InstanceEntry } from "./entity/InstanceEntry.js";
import { fetchInstanceLists, saveLists, addInstance, DEFAULT_LISTS } from "./instanceList.js";

// Populate instance lists onto UI
let instLists = fetchInstanceLists();
for (let [network, list] of Object.entries(instLists)) {
let parent = document.getElementById(`${network}-instance-list`);
let listBottom = parent.querySelector(".instances-reset-button");
const instLists = fetchInstanceLists();
for (const [network, list] of Object.entries(instLists)) {
const parent = document.getElementById(`${network}-instance-list`);
const listBottom = parent.querySelector(".instances-reset-button");
list.forEach((url) => {
let newEntry = new InstanceEntry();
const newEntry = new InstanceEntry();
newEntry.network = network;
newEntry.url = url;
parent.insertBefore(newEntry, listBottom);
Expand All @@ -20,24 +20,24 @@ for (let [network, list] of Object.entries(instLists)) {

// Add event listners for add instance buttons.
// Remove instance buttons handled by InstanceEntry.js
let addInstBtns = Array.from(document.getElementsByClassName("add-instance-btn"));
const addInstBtns = Array.from(document.getElementsByClassName("add-instance-btn"));
addInstBtns.forEach((element) => {
// event handler is async depending on whether or not we re-add instance validation via fetch
element.addEventListener("click", async (event) => {
let btn = event.currentTarget;
let network = btn.getAttribute("data-network");
let input = document.getElementById(`${network}-add-instance`);
const btn = event.currentTarget;
const network = btn.getAttribute("data-network");
const input = document.getElementById(`${network}-add-instance`);
let url = input.value;
// add https:// if the user forgot
if (!(url.includes("//"))) {
url = "https://".concat(url);
}
let success = await addInstance(network, url);
const success = await addInstance(network, url);
if (success) {
let newEntry = new InstanceEntry();
const newEntry = new InstanceEntry();
newEntry.network = network;
newEntry.url = url;
let list = document.getElementById(`${network}-instance-list`);
const list = document.getElementById(`${network}-instance-list`);
list.insertBefore(newEntry, list.children[1]);
input.value = ""; // clear input box after adding to UI
}
Expand All @@ -48,11 +48,11 @@ addInstBtns.forEach((element) => {
});

// Add event listeners for "reset to default"
let resetDefaultBtns = Array.from(document.getElementsByClassName("instances-reset-button"));
const resetDefaultBtns = Array.from(document.getElementsByClassName("instances-reset-button"));
resetDefaultBtns.forEach((element) => {
element.addEventListener("click", (event) => {
let network = event.currentTarget.getAttribute("data-network");
let instanceLists = fetchInstanceLists();
const network = event.currentTarget.getAttribute("data-network");
const instanceLists = fetchInstanceLists();
instanceLists[network] = DEFAULT_LISTS[network];
saveLists(instanceLists);
location.reload();
Expand Down
8 changes: 4 additions & 4 deletions test/paginator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe("Paginator", () => {
paginator = new paginatorImport.Paginator(posts, 2);

// Should display the first two posts
let featuredTagsPosts = document.getElementById("featuredTagsPosts");
const featuredTagsPosts = document.getElementById("featuredTagsPosts");
assert.strictEqual(featuredTagsPosts.children.length, 2);
assert.strictEqual(pageLabel.innerHTML, "Page 1 of 2");
});
Expand All @@ -92,7 +92,7 @@ describe("Paginator", () => {
paginator = new paginatorImport.Paginator(posts, 2);

// Should display the first two posts - before clicking next
let featuredTagsPosts = document.getElementById("featuredTagsPosts");
const featuredTagsPosts = document.getElementById("featuredTagsPosts");
assert.strictEqual(featuredTagsPosts.children.length, 2);

// Should display the next two posts (well actually one since length is 3)
Expand All @@ -118,7 +118,7 @@ describe("Paginator", () => {
paginator = new paginatorImport.Paginator(posts, 2);

// Should display the first two posts - before clicking next
let featuredTagsPosts = document.getElementById("featuredTagsPosts");
const featuredTagsPosts = document.getElementById("featuredTagsPosts");
assert.strictEqual(featuredTagsPosts.children.length, 2);

// Should stay at page 1
Expand All @@ -144,7 +144,7 @@ describe("Paginator", () => {
paginator = new paginatorImport.Paginator(posts, 2);

// Should display the first two posts - before clicking next
let featuredTagsPosts = document.getElementById("featuredTagsPosts");
const featuredTagsPosts = document.getElementById("featuredTagsPosts");
assert.strictEqual(featuredTagsPosts.children.length, 2);

// Should display the next two posts (well actually one since length is 3)
Expand Down
24 changes: 12 additions & 12 deletions test/settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as assert from "assert";
import * as instanceList from "../src/scripts/instanceList.js";

// Mock localStorage
let mockStorage = {
const mockStorage = {
store: {},
getItem(key) {
return (key in this.store) ? this.store[key] : null;
Expand All @@ -18,21 +18,21 @@ let mockStorage = {
const lists = instanceList.fetchInstanceLists(mockStorage);
describe("Fetching instance lists", () => {
it("Should return default lists", () => {
for (let [network, list] of Object.entries(lists)) {
for (const [network, list] of Object.entries(lists)) {
list.forEach(url => assert.ok(instanceList.DEFAULT_LISTS[network].includes(url)));
}
});
});
describe("Removing instances", () => {
it("Don't remove an instance that isn't there", () => {
let ind = instanceList.removeInstance("mastodon", "ligma", mockStorage);
let l2 = instanceList.fetchInstanceLists(mockStorage);
const ind = instanceList.removeInstance("mastodon", "ligma", mockStorage);
const l2 = instanceList.fetchInstanceLists(mockStorage);
assert.strictEqual(ind, -1);
assert.strictEqual(l2["mastodon"].length, lists["mastodon"].length);
});
it("Allows removal of a default instance", () => {
let ind = instanceList.removeInstance("mastodon", instanceList.DEFAULT_LISTS["mastodon"][0], mockStorage);
let l2 = instanceList.fetchInstanceLists(mockStorage);
const ind = instanceList.removeInstance("mastodon", instanceList.DEFAULT_LISTS["mastodon"][0], mockStorage);
const l2 = instanceList.fetchInstanceLists(mockStorage);
assert.strictEqual(ind, 0);
assert.strictEqual(l2["mastodon"].length, lists["mastodon"].length - 1);
});
Expand All @@ -48,16 +48,16 @@ describe("Adding instances", () => {
// assert.strictEqual(before['lemmy'].length, after['lemmy'].length);
// });
it("Reject a malformed URL", async () => {
let before = instanceList.fetchInstanceLists(mockStorage);
let success = await instanceList.addInstance("lemmy", "skidibibopmdada", mockStorage);
let after = instanceList.fetchInstanceLists(mockStorage);
const before = instanceList.fetchInstanceLists(mockStorage);
const success = await instanceList.addInstance("lemmy", "skidibibopmdada", mockStorage);
const after = instanceList.fetchInstanceLists(mockStorage);
assert.strictEqual(success, false);
assert.strictEqual(before["lemmy"].length, after["lemmy"].length);
});
it("Accept a good URL", async () => {
let before = instanceList.fetchInstanceLists(mockStorage);
let success = await instanceList.addInstance("lemmy", "https://mtgzone.com", mockStorage);
let after = instanceList.fetchInstanceLists(mockStorage);
const before = instanceList.fetchInstanceLists(mockStorage);
const success = await instanceList.addInstance("lemmy", "https://mtgzone.com", mockStorage);
const after = instanceList.fetchInstanceLists(mockStorage);
assert.strictEqual(success, true);
assert.strictEqual(before["lemmy"].length + 1, after["lemmy"].length);
});
Expand Down

0 comments on commit 35a4a7f

Please sign in to comment.