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
1import { cache } from "./storage";
2import { cacheInterface } from "./types";
3import * as currencyPackages from "./currencyPackages.json"
4
5export async function pullCache(key: string, replenish: Function, expiry: number, forceReplenish: boolean) {
6 const cacheStorage: cacheInterface = await cache.getValue();
7 const metadata = await cache.getMeta() as { [key: string]: number; };
8
9 const overlap = Date.now() - metadata[key];
10 if (!cacheStorage[key] || forceReplenish || (overlap >= expiry)) {
11 console.info('[Poly+] "' + key + '" cache is stale replenishing...', timeAgo(overlap));
12
13 const replenishedCache = await replenish();
14 cacheStorage[key] = replenishedCache;
15 metadata[key] = Date.now();
16
17 cache.setValue(cacheStorage);
18 cache.setMeta(metadata);
19 }
20
21 return cacheStorage[key];
22};
23
24export async function expireCache(key: string) {
25 console.info('[Poly+] Forcefully expiring "' + key + '" cache...');
26
27 const metadata = await cache.getMeta() as { [key: string]: number };
28 metadata[key] = 0;
29 cache.setMeta(metadata);
30};
31
32function timeAgo(overlap: number) {
33 const units = [
34 { label: 'day', value: 24 * 60 * 60 * 1000 },
35 { label: 'hour', value: 60 * 60 * 1000 },
36 { label: 'min', value: 60 * 1000 },
37 { label: 'sec', value: 1000 },
38 ];
39
40 for (const { label, value } of units) {
41 const count = Math.floor(overlap / value);
42 if (count > 0) {
43 return `${count} ${label}${count > 1 ? 's' : ''} ago`;
44 }
45 overlap %= value;
46 }
47
48 return 'just now';
49};
50
51export async function getUserDetails() {
52 const profileLink: HTMLLinkElement = document.querySelector('.navbar a.text-reset[href^="/users/"]')!;
53 const brickBalance = document.getElementsByClassName('brickBalanceCount')[0];
54
55 if (!profileLink || !brickBalance) return null;
56 return {
57 username: profileLink.innerText.trim(),
58 userId: parseInt(profileLink.href.split('/')[4]),
59 bricks: parseInt(brickBalance.textContent!.replace(/,/g, ""))
60 }
61};
62
63export function bricksToCurrency(bricks: number, currency: string): string | null {
64 if (isNaN(bricks) || bricks == 0) return null;
65
66 const _currencyPackages = currencyPackages as Record<string, Array<Array<number>>>;
67 const packages = _currencyPackages[currency].toSorted((a, b) => b[1] - a[1]);
68
69 if (!packages) {
70 console.warn('[Poly+] Missing currency package data for selected currency!');
71 return null;
72 }
73
74 let totalValue = 0;
75 for (const [currencyValue, bricksValue] of packages) {
76 while (bricks >= bricksValue) {
77 bricks -= bricksValue;
78 totalValue += currencyValue;
79 }
80 }
81
82 if (bricks > 0) {
83 const cheapestPackage = packages[packages.length - 1];
84 const [currencyValue, bricksValue] = cheapestPackage;
85 const unitPrice = currencyValue / bricksValue;
86 totalValue += bricks * unitPrice;
87 }
88
89 return `~${totalValue.toFixed(2)} ${currency}`;
90};