initial commit
This commit is contained in:
137
src/composables/useAlbionPrices.ts
Normal file
137
src/composables/useAlbionPrices.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user