Fully add support for multiple currencies?

This commit is contained in:
kvan7
2025-10-11 11:01:31 -05:00
parent e95ebe6576
commit 1431ce6a06
10 changed files with 117 additions and 73 deletions

View File

@@ -228,7 +228,7 @@
"currency_only_chaos": "Chaos Orb",
"currency_only_div": "Divine Orb",
"currency_chaos_div": "Both Orbs",
"currency_exalted_div": "Both Orbs",
"currency_exalted_div": "Ex/Div Orbs",
"currency_only_exalted": "Exalted Orbs",
"ratio_tooltip": "Ratio used for sorting locally\nDefault on trade site is 7.5:1"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -28,8 +28,8 @@ export interface CurrencyValue {
currency: "chaos" | "exalted" | "div";
}
interface CoreCurrency {
id: string;
export interface CoreCurrency {
id: "exalted" | "chaos";
abbrev: string;
ref: string;
text: string;
@@ -79,10 +79,20 @@ export const usePoeninja = createGlobalState(() => {
return listed;
});
/**
* core/div
*/
const xchgRate = shallowRef<number | undefined>(undefined);
/**
* Current core currency
*/
const xchgRateCurrency = shallowRef<"chaos" | "exalted" | undefined>(
undefined,
);
/**
* exalted/div
*/
const exaltXchgRate = shallowRef<number | undefined>(undefined);
const isLoading = shallowRef(false);
let PRICES_DB: PriceDatabase = [];
@@ -150,24 +160,24 @@ export const usePoeninja = createGlobalState(() => {
ns: "ITEM",
name: "Divine Orb",
});
const preferred =
AppConfig<PriceCheckWidget>("price-check")!.coreCurrency;
const preferred = selectedCoreCurrency.value;
if (divine && divine.exalted >= 30) {
if (preferred === "exalted") {
exaltXchgRate.value = divine.exalted;
if (!preferred || preferred.id === "exalted") {
xchgRate.value = divine.exalted;
xchgRateCurrency.value = "exalted";
} else {
const ex = divine.exalted;
if (preferred === "chaos") {
const chaos = findPriceByQuery({
ns: "ITEM",
name: "Chaos Orb",
});
if (chaos && ex / chaos.exalted >= 5) {
xchgRate.value = ex / chaos.exalted;
xchgRateCurrency.value = "chaos";
}
const ninjaPreferred = findPriceByQuery({
ns: "ITEM",
name: preferred.ref,
});
if (
ninjaPreferred &&
exaltXchgRate.value / ninjaPreferred.exalted >= 5
) {
xchgRate.value = exaltXchgRate.value / ninjaPreferred.exalted;
xchgRateCurrency.value = preferred.id;
}
}
}
@@ -230,34 +240,69 @@ export const usePoeninja = createGlobalState(() => {
return null;
}
function autoCurrency(value: number | [number, number]): CurrencyValue {
/**
* Converts item value from exalts to stable or current core currency
* @param value item value in exalts
* @returns Value in stable or core currency
*/
function autoCurrency(
value: number | [number, number],
useOnlyCore: boolean = false,
): CurrencyValue {
if (Array.isArray(value)) {
if (value[1] > (xchgRate.value || 9999)) {
if (value[1] > (exaltXchgRate.value || 9999) && !useOnlyCore) {
return {
min: exaltedToStable(value[0]),
max: exaltedToStable(value[1]),
min: exaltToStable(value[0]),
max: exaltToStable(value[1]),
currency: "div",
};
}
if (selectedCoreCurrency.value?.id) {
// NOTE: This if should catch everything
return {
min: exaltToCore(value[0]),
max: exaltToCore(value[1]),
currency: selectedCoreCurrency.value.id,
};
}
// this should never run, assuming we have loaded a currency
return { min: value[0], max: value[1], currency: "exalted" };
}
if (value > (xchgRate.value || 9999) * 0.94) {
if (value < (xchgRate.value || 9999) * 1.06) {
if (value > (exaltXchgRate.value || 9999) * 0.94 && !useOnlyCore) {
if (value < (exaltXchgRate.value || 9999) * 1.06) {
return { min: 1, max: 1, currency: "div" };
} else {
return {
min: exaltedToStable(value),
max: exaltedToStable(value),
min: exaltToStable(value),
max: exaltToStable(value),
currency: "div",
};
}
}
if (selectedCoreCurrency.value?.id) {
// NOTE: This if should catch everything
return {
min: exaltToCore(value),
max: exaltToCore(value),
currency: selectedCoreCurrency.value.id,
};
}
// this should never run, assuming we have loaded a currency
return { min: value, max: value, currency: "exalted" };
}
function exaltedToStable(count: number) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function coreToStable(count: number) {
return count / (xchgRate.value || 9999);
}
function exaltToStable(count: number) {
return count / (exaltXchgRate.value || 9999);
}
function exaltToCore(count: number) {
// ex -> core
// ex => ex * (div/ex) * (core/div) = core
return (count / (exaltXchgRate.value || 9999)) * (xchgRate.value || 9999);
}
function cachedCurrencyByQuery(query: DbQuery, count: number) {
const key = { ns: query.ns, name: query.name, count };
@@ -265,7 +310,8 @@ export const usePoeninja = createGlobalState(() => {
return priceCache.get(key)!;
}
const price = findPriceByQuery(query);
const price = // since ex aren't currently in db
query.name === "Exalted Orb" ? { exalted: 1 } : findPriceByQuery(query);
if (!price) {
return;
}

View File

@@ -50,7 +50,7 @@
:title="title"
>
<ui-popover
v-if="stableOrbCost"
v-if="stableOrbCost && xchgRateCurrency?.id"
trigger="click"
boundary="#price-window"
>
@@ -65,13 +65,14 @@
:price="{
min: stableOrbCost,
max: stableOrbCost,
currency: 'exalted',
currency: xchgRateCurrency.id,
}"
item-img="/images/divine.png"
/>
<div v-for="i in 9" :key="i">
<div class="pl-1">
{{ i / 10 }} div ⇒ {{ Math.round((stableOrbCost * i) / 10) }} c
{{ i / 10 }} div ⇒ {{ Math.round((stableOrbCost * i) / 10) }}
{{ xchgRateCurrency.abbrev }}
</div>
</div>
</template>
@@ -271,6 +272,7 @@ export default defineComponent({
const wm = inject<WidgetManager>("wm")!;
const {
xchgRate,
xchgRateCurrency,
initialLoading: xchgRateLoading,
queuePricesFetch,
} = usePoeninja();
@@ -452,6 +454,7 @@ export default defineComponent({
closePriceCheck,
title,
stableOrbCost,
xchgRateCurrency,
xchgRateLoading,
showCheckPos,
checkPosition,

View File

@@ -42,14 +42,10 @@ export default defineComponent({
function getPriceFor(n: number) {
const one = findPriceByQuery(getDetailsId(props.item)!)!;
const price =
props.item.info.refName === "Divine Orb"
? {
min: n * one.exalted,
max: n * one.exalted,
currency: "exalted" as const,
}
: autoCurrency(n * one.exalted);
const price = autoCurrency(
n * one.exalted,
props.item.info.refName === "Divine Orb",
);
return `${displayRounding(price.min)} ${price.currency}`;
}

View File

@@ -5,6 +5,7 @@ const MIN_TTL = 300;
export class Cache {
private cached = new Map<string, unknown>();
private currency = "";
get<T = unknown>(key: unknown): T | undefined {
const _key = hash.sha1(JSON.parse(JSON.stringify(key)));
@@ -20,6 +21,13 @@ export class Cache {
}, ttl * 1000);
}
purgeIfDifferentCurrency(currency: string | undefined) {
if (!currency || this.currency === currency) return;
this.currency = currency;
this.cached.clear();
console.log("Purged cache");
}
static deriveTtl(...limits: RateLimiter[]): number {
return Math.max(MIN_TTL, ...limits.map((limit) => limit.window));
}

View File

@@ -84,9 +84,9 @@
<ui-radio v-model="filters.trade.currency" value="exalted">{{
t(":currency_only_exalted")
}}</ui-radio>
<!-- <ui-radio v-model="filters.trade.currency" value="chaos">{{
<ui-radio v-model="filters.trade.currency" value="chaos">{{
t(":currency_only_chaos")
}}</ui-radio> -->
}}</ui-radio>
<ui-radio v-model="filters.trade.currency" value="divine">{{
t(":currency_only_div")
}}</ui-radio>

View File

@@ -12,16 +12,11 @@
}"
>{{ result.priceAmount }} {{ result.priceCurrency
}}{{
result.priceCurrency !== "exalted" &&
result.normalizedPriceCurrency &&
result.priceCurrency !== result.normalizedPriceCurrency.id &&
result.priceCurrency !== "divine" &&
result.normalizedPrice
? ` (${result.normalizedPrice} ${
result.normalizedPriceCurrency! === "exalted"
? "ex"
: result.normalizedPriceCurrency! === "chaos"
? "c"
: result.normalizedPriceCurrency!
})`
? ` (${result.normalizedPrice} ${result.normalizedPriceCurrency.abbrev})`
: ""
}}</span
>
@@ -31,18 +26,6 @@
><span class="font-sans">×</span> {{ result.listedTimes }}</span
><i v-else-if="!result.hasNote" class="fas fa-question" />
</td>
<!-- <td class="px-2">
<div v-if="result.priceCurrencyRank" class="my-2 flex flex-row">
<div
v-for="i in result.priceCurrencyRank"
class="account-status mr-1"
:class="{
'rank-2': result.priceCurrencyRank === 2,
'rank-3': result.priceCurrencyRank === 3,
}"
></div>
</div>
</td> -->
<td v-if="item.stackSize" class="px-2 text-right">
{{ result.stackSize }}
</td>

View File

@@ -310,7 +310,13 @@ export interface PricingResult {
priceCurrency: string;
priceCurrencyRank?: number;
normalizedPrice?: string;
normalizedPriceCurrency?: string;
normalizedPriceCurrency?: {
id: "exalted" | "chaos";
abbrev: string;
ref: string;
text: string;
icon: string;
};
isMine: boolean;
hasNote: boolean;
isInstantBuyout: boolean;
@@ -953,8 +959,11 @@ export async function requestResults(
resultIds: string[],
opts: { accountName: string },
): Promise<PricingResult[]> {
const { cachedCurrencyByQuery, xchgRateCurrency } = usePoeninja();
// Solves cached results showing random incorrect values
cache.purgeIfDifferentCurrency(xchgRateCurrency.value?.id);
let data = cache.get<FetchResult[]>(resultIds);
const { cachedCurrencyByQuery } = usePoeninja();
if (!data) {
await RateLimiter.waitMulti(RATE_LIMIT_RULES.FETCH);
@@ -1053,21 +1062,15 @@ export async function requestResults(
const query = getCurrencyDetailsId(
result.listing.price?.currency ?? "no price",
);
const normalizedCurrency =
result.listing.price?.currency === "exalted"
? // exalts aren't in db since they are the stable currency
{
min: result.listing.price.amount,
max: result.listing.price.amount,
currency: "exalted",
}
: // otherwise convert to stable
cachedCurrencyByQuery(query, result.listing.price?.amount ?? 0);
const normalizedCurrency = cachedCurrencyByQuery(
query,
result.listing.price?.amount ?? 0,
);
const normalizedPrice =
normalizedCurrency !== undefined
? displayRounding(normalizedCurrency.min)
: undefined;
const normalizedPriceCurrency = normalizedCurrency?.currency;
const normalizedPriceCurrency = xchgRateCurrency.value;
return {
id: result.id,

View File

@@ -40,6 +40,11 @@
src="/images/chaos.png"
class="max-w-full max-h-full"
/>
<img
v-else-if="price?.currency === 'annul'"
src="/images/annul.png"
class="max-w-full max-h-full"
/>
<img v-else src="/images/exa.png" class="max-w-full max-h-full" />
</div>
</div>
@@ -56,7 +61,7 @@ export default defineComponent({
type: Object as PropType<{
min: number;
max: number;
currency: "chaos" | "div" | "exalted";
currency: string;
}>,
default: undefined,
},