Skip to content

Commit

Permalink
Code reorganization & cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
BWBama85 committed Jun 28, 2023
1 parent b77a0ba commit 52e9752
Show file tree
Hide file tree
Showing 17 changed files with 755 additions and 655 deletions.
Binary file modified build/chrome-mv3-prod.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "airline-club-plus",
"displayName": "Airline Club Plus",
"version": "1.3.1",
"version": "1.4.0",
"description": "Improves the Airline-Club.com game experience.",
"author": "Brent Wilson <[email protected]>",
"scripts": {
Expand Down
40 changes: 17 additions & 23 deletions src/modules/OilOverlay.tsx → src/components/OilOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import type { PlasmoCSUIProps } from "plasmo"
import { useEffect, useState } from "react"
import { useEffect, useMemo, useState } from "react"
import type { FC } from "react"

import { Storage } from "@plasmohq/storage"

import { getStyleFromTier, getTierFromPercent } from "~helpers/utils"

interface OilData {
cycle: number
price: number
}
import { formatCurrency, getStyleFromTier, getTierFromPercent } from "~helpers/utils"

const storage = new Storage()

const OilOverlay: FC<PlasmoCSUIProps> = () => {
const [oilPrice, setOilPrice] = useState<number>()
const [className, setClassName] = useState("latestOilPriceShortCut clickable")
const [state, setState] = useState({
oilPrice: undefined,
className: "latestOilPriceShortCut clickable"
})

useEffect(() => {
const checkLocalStorageAndUpdate = async () => {
const oilData = (await storage.get("oilData")) as OilData[]
if (oilData) {
const latestPrice = oilData.slice(-1)[0].price
setOilPrice(latestPrice)
const tierForPrice = 5 - getTierFromPercent(latestPrice, 40, 80)
setState({
oilPrice: latestPrice,
className: tierForPrice < 2 ? "latestOilPriceShortCut clickable glow" : "latestOilPriceShortCut clickable"
})
}
}

Expand All @@ -34,25 +35,18 @@ const OilOverlay: FC<PlasmoCSUIProps> = () => {
}
}, [])

const tierForPrice = oilPrice ? 5 - getTierFromPercent(oilPrice, 40, 80) : undefined

useEffect(() => {
if (tierForPrice < 2) {
setClassName("latestOilPriceShortCut clickable glow")
} else {
setClassName("latestOilPriceShortCut clickable")
}
}, [tierForPrice])

const oilPriceStyle = getStyleFromTier(tierForPrice)
const oilPriceStyle = useMemo(
() => getStyleFromTier(state.oilPrice ? 5 - getTierFromPercent(state.oilPrice, 40, 80) : undefined),
[state.oilPrice]
)

return (
<span
style={{ marginLeft: 10, position: "relative", display: "inline-block" }}
className={className}
className={state.className}
title="Latest Oil Price">
<span className="latest-price label" style={{ ...oilPriceStyle }}>
{oilPrice ? `$${new Intl.NumberFormat("en-US").format(oilPrice)}` : "Loading..."}
{state.oilPrice ? `${formatCurrency(state.oilPrice)}` : "Loading..."}
</span>
</span>
)
Expand Down
191 changes: 73 additions & 118 deletions src/contents/airplanes.ts
Original file line number Diff line number Diff line change
@@ -1,139 +1,94 @@
import type { PlasmoCSConfig } from "plasmo"

import {
addInputCells,
addTableHeaderCells,
calculateCosts,
calculateUtilisation,
isModelOwned,
isValidModel,
setupNewDataFilterElements
} from "~/helpers/airplane"
import { createCellContents, createRowElement, populateTableCells } from "~/helpers/tables"
import { calcFlightTime, calcFuelBurn, createLoadingElement, sortByProperty } from "~/helpers/utils"
import { loadAirplaneModelStats } from "~/modules/loadAirplaneModelStats"
import { updateAirplaneModelTable } from "~/modules/updateAirplaneModelTable"

export const config: PlasmoCSConfig = {
world: "MAIN",
matches: ["https://*.airline-club.com/*"],
run_at: "document_start"
}

window.addEventListener("DOMContentLoaded", async () => {
addInputCells()

addTableHeaderCells()

setupNewDataFilterElements()

window.updateAirplaneModelTable = async function (sortProperty: string, sortOrder: string): Promise<void> {
const distance = Number((document.getElementById("fightRange") as HTMLInputElement).value)
const runway = Number((document.getElementById("runway") as HTMLInputElement).value)
const min_capacity = Number((document.getElementById("min_capacity") as HTMLInputElement).value)
const min_circulation = Number((document.getElementById("min_circulation") as HTMLInputElement).value)
const use_flight_total = (document.getElementById("fuel_total") as HTMLInputElement).checked
const airplaneModelTable = document.getElementById("airplaneModelTable")

const airplaneTypeMap = {
LIGHT: 1,
SMALL: 1,
REGIONAL: 3,
MEDIUM: 8,
LARGE: 12,
"EXTRA LARGE": 15,
X_LARGE: 15,
JUMBO: 18,
SUPERSONIC: 12
}
let plane: Plane
for (plane of Object.values(window.loadedModelsOwnerInfo)) {
if (plane.range < distance || plane.runwayRequirement > runway) {
plane.cpp = -1
plane.max_rotation = -1
continue
}

const plane_category = airplaneTypeMap[plane.airplaneType.toUpperCase()]
const flightDuration = calcFlightTime(plane, distance)

const { utilisation, frequency } = calculateUtilisation(flightDuration, plane.turnaroundTime)
const { depreciationRate, maintenance, airport_fee, crew_cost, inflight_cost } = calculateCosts(
plane,
flightDuration,
plane_category,
utilisation
)

plane.max_rotation = frequency
plane.fbpf = calcFuelBurn(plane, distance)
plane.fbpp = plane.fbpf / plane.capacity
plane.fbpw = plane.fbpf * plane.max_rotation
plane.fuel_total =
(plane.fbpf * 0.08 + airport_fee + inflight_cost + crew_cost) * plane.max_rotation +
depreciationRate +
maintenance
plane.cpp = plane.fuel_total / (plane.capacity * plane.max_rotation)
plane.max_capacity = plane.capacity * plane.max_rotation
plane.fuel_total_info =
"Total Frequency Cost: $" +
new Intl.NumberFormat("en-US").format(Math.round(plane.fuel_total)) +
" / Per Flight: $" +
new Intl.NumberFormat("en-US").format(Math.round(plane.cpp * plane.capacity))

if (plane.in_use === undefined) {
plane.in_use = -1

const loadingElement = createLoadingElement()
const airplaneModelTable = document.getElementById("airplaneModelTable")
airplaneModelTable?.appendChild(loadingElement)

try {
await window.loadAirplaneModelStats(plane, { totalOnly: true })
} finally {
loadingElement.parentNode.removeChild(loadingElement)
}
}
}

if (!sortProperty && !sortOrder) {
const selectedSortHeader = document.querySelector("#airplaneModelSortHeader .cell.selected") as HTMLElement
sortProperty = selectedSortHeader.getAttribute("data-sort-property")
if (sortProperty === "capacity") {
sortProperty = "max_capacity"
} else if (sortProperty === "cpp" && use_flight_total) {
sortProperty = "fuel_total"
}
sortOrder = selectedSortHeader.getAttribute("data-sort-order")
}
console.log(sortProperty, sortOrder)
//sort the list
window.loadedModelsOwnerInfo.sort(sortByProperty(sortProperty, sortOrder == "ascending"))
console.log(sortProperty, sortOrder)

var childElements = airplaneModelTable.querySelectorAll("div.table-row")
function addInputCell(id: string, labelText: string, mainPanel: HTMLElement, value: string = "0"): void {
const div = document.createElement("div")
div.classList.add("cell", "detailsSelection")
div.innerHTML = `${labelText}: <input type="text" id="${id}" value="${value}" />`
mainPanel.appendChild(div)
}

childElements.forEach(function (child) {
airplaneModelTable.removeChild(child)
})
function addCellToHeader(sortProperty: string, content: string, airplaneModelSortHeader: HTMLElement): void {
const div = document.createElement("div")
div.classList.add("cell", "clickable")
div.setAttribute("data-sort-property", sortProperty)
div.setAttribute("data-sort-order", "ascending")
div.style.textAlign = "right"
div.textContent = content
div.setAttribute("onclick", "toggleAirplaneModelTableSortOrder($(this))")
airplaneModelSortHeader.appendChild(div)
}

let modelOwnerInfo: Plane
for (modelOwnerInfo of Object.values(window.loadedModelsOwnerInfo)) {
modelOwnerInfo.in_use = modelOwnerInfo.in_use || 0
window.addEventListener("DOMContentLoaded", async () => {
const hangarDiv = document.querySelector('div[data-type="hangar"]')
const mainPanel: HTMLElement = document.querySelector(
"#airplaneCanvas .mainPanel .section .table .table-header:first-child"
)

if (hangarDiv && !hangarDiv.classList.contains("selected") && mainPanel) {
// Remove the style attribute
hangarDiv.removeAttribute("style")

addInputCell("fightRange", "Distance", mainPanel, "1000")
addInputCell("runway", "Runway length", mainPanel, "3600")
addInputCell("min_capacity", "Min. Capacity", mainPanel)
addInputCell("min_circulation", "Min. Circulation", mainPanel)

// Additional adjustments for the selectionDiv
const selectionDiv = document.createElement("div")
selectionDiv.classList.add("cell", "detailsSelection")
selectionDiv.style.minWidth = "160px"
selectionDiv.style.textAlign = "right"
selectionDiv.innerHTML = `<label for="fuel_total">Flight Fuel Total <input type="checkbox" id="fuel_total" /></label>`
mainPanel.appendChild(selectionDiv)
}

const isOwned = isModelOwned(modelOwnerInfo)
const columnWidthPercents: number[] = [17, 12, 9, 7, 7, 7, 7, 9, 7, 6, 3, 5]
const airplaneModelSortHeader: HTMLElement = document.getElementById("airplaneModelSortHeader")

if (!isValidModel(modelOwnerInfo, min_capacity, min_circulation)) {
continue
}
if (airplaneModelSortHeader) {
addCellToHeader("max_rotation", "⏲", airplaneModelSortHeader)
addCellToHeader("cpp", "$/🧍", airplaneModelSortHeader)
addCellToHeader("in_use", "#✈", airplaneModelSortHeader)
}

const row = createRowElement(modelOwnerInfo, isOwned)
const cells = createCellContents(modelOwnerInfo)
const headerCells = document.querySelectorAll("#airplaneModelSortHeader .cell")
headerCells.forEach((headerCell, index) => {
headerCell.setAttribute("style", `width: ${columnWidthPercents[index]}%`)
})

populateTableCells(row, cells, modelOwnerInfo.fuel_total_info)
const airplaneModelTable = document.querySelector("#airplaneModelTable .table-header")
if (airplaneModelTable) {
let html = ""
columnWidthPercents.forEach((width) => {
html += `<div class="cell" style="width: ${width}%; border-bottom: none;"></div>`
})
airplaneModelTable.innerHTML = html
}

airplaneModelTable.appendChild(row)
const totalOwnedElems = document.querySelectorAll('[data-sort-property="totalOwned"]')
totalOwnedElems.forEach((elem) => {
elem.textContent = "Owned"
elem.setAttribute("style", "width: 6%;")
})

const newDataFilterElements: string[] = ["#fightRange", "#runway", "#min_capacity", "#min_circulation", "#fuel_total"]
newDataFilterElements.forEach((id) => {
const element = document.querySelector(id)
if (element instanceof HTMLElement) {
element.addEventListener("change", () => window.updateAirplaneModelTable(element.id, "descending"))
}
}
})

window.updateAirplaneModelTable = updateAirplaneModelTable

window.loadAirplaneModelStats = loadAirplaneModelStats
})
2 changes: 1 addition & 1 deletion src/contents/oilPriceDesktop.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { PlasmoCSConfig, PlasmoCSUIJSXContainer, PlasmoRender } from "plasmo"
import { createRoot } from "react-dom/client"

import OilOverlay from "~/modules/OilOverlay"
import OilOverlay from "~/components/OilOverlay"

export const config: PlasmoCSConfig = {
matches: ["https://*.airline-club.com/*"]
Expand Down
2 changes: 1 addition & 1 deletion src/contents/oilPriceMobile.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { PlasmoCSConfig, PlasmoCSUIJSXContainer, PlasmoRender } from "plasmo"
import { createRoot } from "react-dom/client"

import OilOverlay from "~/modules/OilOverlay"
import OilOverlay from "~/components/OilOverlay"

export const config: PlasmoCSConfig = {
matches: ["https://*.airline-club.com/*"]
Expand Down
38 changes: 38 additions & 0 deletions src/helpers/CacheClass.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* `CacheClass` is a simple in-memory cache system.
* This class stores key-value pairs where keys are strings and values are numbers.
* It provides methods to set, get, and check existence of a key-value pair in the cache.
*/
export class CacheClass {
private cache: { [key: string]: number } = {}

/**
* Stores a number value in the cache associated with a specified string key.
*
* @param {string} key - The key under which to store the value.
* @param {number} value - The value to be stored.
*/
set(key: string, value: number): void {
this.cache[key] = value
}

/**
* Retrieves a number value from the cache associated with a specified string key.
*
* @param {string} key - The key for which to retrieve the value.
* @returns {number} The value associated with the key, or undefined if the key does not exist.
*/
get(key: string): number {
return this.cache[key]
}

/**
* Checks whether a specific key exists in the cache.
*
* @param {string} key - The key to check for existence.
* @returns {boolean} Returns true if the key exists, false otherwise.
*/
has(key: string): boolean {
return this.cache.hasOwnProperty(key)
}
}
Loading

0 comments on commit 52e9752

Please sign in to comment.