Files
albion-crafting-calc/src/composables/useAlbionPrices.ts
2026-03-04 17:01:08 -05:00

138 lines
5.0 KiB
TypeScript

import { ref, readonly } from 'vue'
import type { AlbionPriceEntry, AlbionCity, ManualPriceEntry, ManualPriceCache } from '../types/api'
// ─── Override duration ────────────────────────────────────────────────────────
export const OVERRIDE_DURATION_OPTIONS: { label: string; ms: number }[] = [
{ label: '30 min', ms: 30 * 60 * 1000 },
{ label: '1 hour', ms: 60 * 60 * 1000 },
{ label: '2 hours', ms: 2 * 60 * 60 * 1000 },
{ label: '4 hours', ms: 4 * 60 * 60 * 1000 },
{ label: '8 hours', ms: 8 * 60 * 60 * 1000 },
{ label: '24 hours',ms: 24 * 60 * 60 * 1000 },
]
const DURATION_STORAGE_KEY = 'albion-override-duration'
const MANUAL_STORAGE_KEY = 'albion-manual-prices'
function loadOverrideDuration(): number {
try {
const raw = localStorage.getItem(DURATION_STORAGE_KEY)
if (raw) {
const ms = Number(raw)
if (OVERRIDE_DURATION_OPTIONS.some(o => o.ms === ms)) return ms
}
} catch { /* ignore */ }
return 24 * 60 * 60 * 1000 // default 24 hours
}
function loadManualPricesFromStorage(): ManualPriceCache {
try {
const raw = localStorage.getItem(MANUAL_STORAGE_KEY)
if (raw) {
const obj = JSON.parse(raw) as Record<string, ManualPriceEntry>
return new Map(Object.entries(obj))
}
} catch { /* ignore */ }
return new Map()
}
function saveManualPricesToStorage(map: ManualPriceCache): void {
try {
const obj: Record<string, ManualPriceEntry> = {}
for (const [key, val] of map) obj[key] = val
localStorage.setItem(MANUAL_STORAGE_KEY, JSON.stringify(obj))
} catch { /* ignore */ }
}
// ─── Module-level singleton state ────────────────────────────────────────────
const manualPrices = ref<ManualPriceCache>(loadManualPricesFromStorage())
const overrideDurationMs = ref<number>(loadOverrideDuration())
// ─── Helpers ──────────────────────────────────────────────────────────────────
function cacheKey(itemId: string, city: string): string {
return `${itemId}::${city}`
}
function isManualExpired(entry: ManualPriceEntry): boolean {
return Date.now() - new Date(entry.editedAt).getTime() > overrideDurationMs.value
}
function synthesizeManualEntry(
entry: ManualPriceEntry,
itemId: string,
city: AlbionCity,
): AlbionPriceEntry {
return {
item_id: itemId,
city,
quality: 1,
sell_price_min: entry.sell_price_min,
sell_price_min_date: entry.editedAt,
sell_price_max: entry.sell_price_min,
buy_price_min: 0,
buy_price_max: 0,
}
}
// ─── Price accessors ──────────────────────────────────────────────────────────
function getPrice(itemId: string, city: AlbionCity): AlbionPriceEntry | null {
const key = cacheKey(itemId, city)
const manual = manualPrices.value.get(key)
if (manual && !isManualExpired(manual)) {
return synthesizeManualEntry(manual, itemId, city)
}
return null
}
// ─── Manual price management ──────────────────────────────────────────────────
function setManualPrice(itemId: string, city: AlbionCity, price: number): void {
const key = cacheKey(itemId, city)
const next = new Map(manualPrices.value)
next.set(key, { sell_price_min: price, editedAt: new Date().toISOString() })
manualPrices.value = next
saveManualPricesToStorage(next)
}
function clearManualPrice(itemId: string, city: AlbionCity): void {
const key = cacheKey(itemId, city)
if (!manualPrices.value.has(key)) return
const next = new Map(manualPrices.value)
next.delete(key)
manualPrices.value = next
saveManualPricesToStorage(next)
}
function isManualPrice(itemId: string, city: AlbionCity): boolean {
const entry = manualPrices.value.get(cacheKey(itemId, city))
return !!entry && !isManualExpired(entry)
}
function getManualEntry(itemId: string, city: AlbionCity): ManualPriceEntry | undefined {
const entry = manualPrices.value.get(cacheKey(itemId, city))
return entry && !isManualExpired(entry) ? entry : undefined
}
function setOverrideDuration(ms: number): void {
overrideDurationMs.value = ms
try { localStorage.setItem(DURATION_STORAGE_KEY, String(ms)) } catch { /* ignore */ }
}
// ─── Public API ───────────────────────────────────────────────────────────────
export function useAlbionPrices() {
return {
overrideDurationMs: readonly(overrideDurationMs),
getPrice,
setManualPrice,
clearManualPrice,
isManualPrice,
getManualEntry,
setOverrideDuration,
}
}