A rewrite of Poly+, my quality-of-life browser extension for Polytoria. Built entirely fresh using the WXT extension framework, Typescript, and with added better overall code quality.
extension

feat: restructure API calls into central file + config JSON

Index 912667ef cbc3c341

Changed files
+85 -33
entrypoints
utils
+20 -32
entrypoints/home.content.ts
···
import { preferences, _favoritedPlaces, _bestFriends } from "@/utils/storage";
import { placeApiSchema, userApiSchema } from "@/utils/types";
import { pullCache } from "@/utils/utilities";
+
import api from "@/utils/api";
export default defineContentScript({
matches: ['https://polytoria.com/', 'https://polytoria.com/home'],
main() {
-
preferences.getValue()
+
preferences.getPreferences()
.then((values) => {
if (values.favoritedPlaces.enabled) favoritedPlaces();
if (values.bestFriends.enabled) bestFriends();
···
column.insertBefore(container, column.children[1]);
}
-
const placeData: Array<placeApiSchema> = await pullCache('favoritedPlaces', async () => {
-
// API is not viable right now, static data used in it's place for now.
-
return {
-
"9656": {"id":9656,"name":"The Wayland Bridge","description":"Listen up troops,\r\n\r\nOn this battlefield your mission is to capture the enemy flag and perform as many casualties to the enemy team. The first team to reach 5 captures will claim victory. The high order will reward you for your confirmed kills, use this wisely to upgrade your arsenal.\r\n\r\nGood luck soldier.","creator":{"type":"user","id":1,"name":"Polytoria","thumbnail":"https://c0.ptacdn.com/thumbnails/avatars/4f42532128b4b6938bb4a01b0f74537ebf5e9efaa09b88d0bf7d343de536c46e-icon.png"},"thumbnail":"https://c0.ptacdn.com/places/icons/WxomBAbMNNNnnAkKxuD11Snnoz2C26wW.png","genre":"fighting","maxPlayers":24,"isActive":false,"isToolsEnabled":true,"isCopyable":false,"visits":10205,"uniqueVisits":799,"playing":0,"rating":{"likes":149,"dislikes":49,"percent":"75%"},"accessType":"everyone","accessPrice":null,"createdAt":"2024-06-14T07:48:36.720+00:00","updatedAt":"2024-07-14T09:07:53.198+00:00"}
-
};
-
-
/*
-
const res: any = {}
-
for (let id of places as number[]) {
-
const info = (await (await fetch('https://api.polytoria.com/v1/places/' + id)).json())
-
res[id] = info
-
}
-
return res;
-
*/
-
}, 300000, true);
+
const placeData: Array<placeApiSchema> = await pullCache(
+
'favoritedPlaces',
+
async () => await api.places.batch!(places as string[]),
+
300000,
+
false
+
);
const card = container.getElementsByClassName('scrollFadeContainer')[0]
for (let i = 0; i < places.length; i++) {
···
}
card.children[0].remove();
card.classList.add('d-flex');
-
Array.from(card.children).forEach((place) => {place.classList.remove('d-none')});
+
Array.from(card.children).forEach((place) => { place.classList.remove('d-none') });
});
}
···
_bestFriends.getValue()
.then(async (friends) => {
-
const userData = await pullCache('bestFriends', async () => {
-
// API is not viable right now, static data used in it's place for now.
-
return {
-
"1": {"id":1,"username":"Polytoria","description":"Welcome to the Polytoria profile!\r\nFor inquiries or support, please send a message to one of our staff members. Messages sent to this account will not be read!","signature":"Hello!","thumbnail":{"avatar":"https://c0.ptacdn.com/thumbnails/avatars/4f42532128b4b6938bb4a01b0f74537ebf5e9efaa09b88d0bf7d343de536c46e.png","icon":"https://c0.ptacdn.com/thumbnails/avatars/4f42532128b4b6938bb4a01b0f74537ebf5e9efaa09b88d0bf7d343de536c46e-icon.png"},"playing":null,"netWorth":3991,"placeVisits":20461,"profileViews":2567,"forumPosts":17,"assetSales":64172,"membershipType":"plusDeluxe","isStaff":true,"registeredAt":"2019-04-14T12:29:52.000+00:00","lastSeenAt":"2024-11-21T11:34:14.119+00:00"}
-
};
-
-
/*
-
const res: any = {}
-
for (let id of friends as number[]) {
-
const info = (await (await fetch('https://api.polytoria.com/v1/users/' + id)).json())
-
res[id] = info
-
}
-
return res;
-
*/
-
}, 300000, true);
+
const userData = await pullCache(
+
'bestFriends',
+
async () => await api.users.batch!(friends as string[]),
+
300000,
+
false
+
);
for (const id of friends) {
+
if (!userData[id]) {
+
console.warn("[Poly+] Missing cached user data for ID " + id);
+
continue;
+
};
+
let headshot = document.getElementById('friend-' + id);
if (!headshot) headshot = await createHeadshot(id);
friendsRow.prepend(headshot, friendsRow.children[0]);
+33
utils/api.ts
···
+
import config from "./config.json";
+
import { userApiSchema, placeApiSchema } from "./types";
+
+
const users: {
+
batch?: (ids: string[]) => Promise<Record<string, userApiSchema>>
+
} = {};
+
+
const places: {
+
batch?: (ids: string[]) => Promise<Record<string, placeApiSchema>>
+
} = {};
+
+
users.batch = async (ids: string[]): Promise<Record<string, userApiSchema>> => {
+
const res: Record<string, userApiSchema> = {};
+
for (let id of ids) {
+
const info = await (await fetch(config.api.url + '/users/' + id)).json();
+
res[id] = info;
+
}
+
return res;
+
};
+
+
places.batch = async (ids: string[]): Promise<Record<string, placeApiSchema>> => {
+
const res: Record<string, placeApiSchema> = {};
+
for (let id of ids) {
+
const info = await (await fetch(config.api.url + '/places/' + id)).json();
+
res[id] = info;
+
}
+
return res;
+
};
+
+
export default {
+
users: users,
+
places: places
+
};
+8
utils/config.json
···
+
{
+
"version": "0.0.1",
+
"devBuild": true,
+
"api": {
+
"url": "https://api.polytoria.com/v1/",
+
"fallback": false
+
}
+
}
+24 -1
utils/storage.ts
···
+
import { WxtStorageItem } from "wxt/storage";
+
export const defaultPreferences = {
favoritedPlaces: { enabled: true },
bestFriends: { enabled: true },
···
};
// Sync
-
export const preferences = storage.defineItem('sync:preferences', { fallback: defaultPreferences, version: 1 });
+
interface PreferencesStorageItem extends WxtStorageItem<typeof defaultPreferences, {}> {
+
/**
+
* Wrapper for the standard `.getValue()` method that merges the user's saved preferences with the default preferences to make sure there are no unexpected errors.
+
*/
+
getPreferences: () => Promise<typeof defaultPreferences>;
+
}
+
+
export const preferences: PreferencesStorageItem = storage.defineItem('sync:preferences', { fallback: defaultPreferences, version: 1 }) as PreferencesStorageItem;
export const _favoritedPlaces = storage.defineItem('sync:favoritedPlaces', { fallback: ["9656"], version: 1 });
export const _bestFriends = storage.defineItem('sync:bestFriends', { fallback: ["2782"], version: 1 });
+
+
//@ts-ignore: Custom method for merging settings and defaults
+
/*
+
const _getValue = preferences.getValue;
+
preferences.getValue = async function() {
+
const userPreferences = await _getValue.call(this);
+
return { ...defaultPreferences, ...userPreferences };
+
};
+
*/
+
+
preferences.getPreferences = async function() {
+
const userPreferences = await this.getValue();
+
return { ...defaultPreferences, ...userPreferences };
+
};
// Cache
export const cache = storage.defineItem('local:cache', {