mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
currency icons
This commit is contained in:
1 parent
f0e0d849d5
commit
d9ce16b41a
6 files changed
+83
-35
No files matched your search
@@ -36,13 +36,12 @@ function PriceEntry({ entry }: PriceEntryProps) {
|
|||||||
throw new Error("Unsupported price type");
|
throw new Error("Unsupported price type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [, iconName] = entry.currency_price.currency_catalog_id.split(".");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text size="minor" lowContrast className={styles.entry}>
|
<Text size="minor" lowContrast className={styles.entry}>
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<img
|
<img className={styles.icon} src={`/api/currencies/${iconName}.webp`} />
|
||||||
className={styles.icon}
|
|
||||||
src="https://static.vecteezy.com/system/resources/thumbnails/047/493/988/small/hairy-fluffy-cat-playing-png.png"
|
|
||||||
/>
|
|
||||||
{formatCompact(locale, entry.currency_price.amount)}
|
{formatCompact(locale, entry.currency_price.amount)}
|
||||||
</div>
|
</div>
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -11,13 +11,17 @@
|
|||||||
gap: var(--space-3);
|
gap: var(--space-3);
|
||||||
|
|
||||||
.stage {
|
.stage {
|
||||||
|
padding: var(--space-3);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
aspect-ratio: 3 / 4;
|
||||||
|
|
||||||
|
font-size: 2em;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
|
|
||||||
transform: translateX(var(--space-1));
|
transform: translate(var(--space-1), calc(-1 * var(--space-1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier {
|
.tier {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { useProtagonist } from "../../hooks/useProtagonist";
|
|||||||
import { useStrings } from "../../hooks/useStrings";
|
import { useStrings } from "../../hooks/useStrings";
|
||||||
import { useUpgradePreset } from "../../hooks/useUpgradePreset";
|
import { useUpgradePreset } from "../../hooks/useUpgradePreset";
|
||||||
import { Tankopedia } from "../../stores/tankopedia";
|
import { Tankopedia } from "../../stores/tankopedia";
|
||||||
|
import { Button } from "../Button";
|
||||||
import { Heading } from "../Heading";
|
import { Heading } from "../Heading";
|
||||||
import { IconButton } from "../IconButton";
|
|
||||||
import { Price } from "../Price";
|
import { Price } from "../Price";
|
||||||
import { Section } from "../Section";
|
import { Section } from "../Section";
|
||||||
import { Text } from "../Text";
|
import { Text } from "../Text";
|
||||||
@@ -117,7 +117,7 @@ function LineElement({ index, lineName, stage }: LineElementProps) {
|
|||||||
const isSelected = upgrades[lineName] === index;
|
const isSelected = upgrades[lineName] === index;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<Button
|
||||||
color={isSelected ? undefined : "gray"}
|
color={isSelected ? undefined : "gray"}
|
||||||
variant={isSelected ? "surface" : "soft"}
|
variant={isSelected ? "surface" : "soft"}
|
||||||
radius="1"
|
radius="1"
|
||||||
@@ -183,6 +183,6 @@ function LineElement({ index, lineName, stage }: LineElementProps) {
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{price && <Price className={styles.price} price={price} />}
|
{price && <Price className={styles.price} price={price} />}
|
||||||
</IconButton>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,8 @@ export abstract class AbstractAPI {
|
|||||||
abstract background(id: string): Promise<Background>;
|
abstract background(id: string): Promise<Background>;
|
||||||
abstract backgrounds(): Promise<Backgrounds>;
|
abstract backgrounds(): Promise<Backgrounds>;
|
||||||
|
|
||||||
|
abstract currencyIcon(id: string): Promise<string>;
|
||||||
|
|
||||||
private assertLocale(locale: string) {
|
private assertLocale(locale: string) {
|
||||||
const supported = locales.supported.find(
|
const supported = locales.supported.find(
|
||||||
(supported) => supported.locale === locale,
|
(supported) => supported.locale === locale,
|
||||||
|
|||||||
@@ -35,6 +35,40 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolveRemoteUrl(remoteUrls: string[]) {
|
||||||
|
let remoteStorage: RemoteStorageComponent | undefined = undefined;
|
||||||
|
|
||||||
|
for (const remoteUrl of remoteUrls) {
|
||||||
|
const item = this.metadata.item(remoteUrl);
|
||||||
|
const candidate = item.RemoteStorage();
|
||||||
|
|
||||||
|
if (
|
||||||
|
remoteStorage === undefined ||
|
||||||
|
candidate.download_speed_weight > remoteStorage.download_speed_weight
|
||||||
|
) {
|
||||||
|
remoteStorage = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remoteStorage === undefined) {
|
||||||
|
throw new Error("No suitable production remote storage found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveClientConfig() {
|
||||||
|
const group = this.metadata.group("ClientConfigsEntity");
|
||||||
|
|
||||||
|
if (group.length !== 1) {
|
||||||
|
throw new RangeError(
|
||||||
|
`Don't know how to handle ${group.length} ClientConfigsEntities`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.metadata.item(group[0].id);
|
||||||
|
}
|
||||||
|
|
||||||
@Cache()
|
@Cache()
|
||||||
async tankList() {
|
async tankList() {
|
||||||
const group = this.metadata.group("TankEntity");
|
const group = this.metadata.group("TankEntity");
|
||||||
@@ -207,37 +241,14 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
|
|
||||||
@Cache()
|
@Cache()
|
||||||
async gameStrings(locale: string) {
|
async gameStrings(locale: string) {
|
||||||
const group = this.metadata.group("ClientConfigsEntity");
|
const clientConfig = this.resolveClientConfig();
|
||||||
|
|
||||||
if (group.length !== 1) {
|
|
||||||
throw new RangeError(
|
|
||||||
`Don't know how to handle ${group.length} ClientConfigsEntities`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientConfig = this.metadata.item(group[0].id);
|
|
||||||
const localizationResources = clientConfig.LocalizationResources();
|
const localizationResources = clientConfig.LocalizationResources();
|
||||||
const strings: Record<string, string> = {};
|
const strings: Record<string, string> = {};
|
||||||
|
|
||||||
for (const remoteStorageSettings of localizationResources.remote_storages) {
|
for (const remoteStorageSettings of localizationResources.remote_storages) {
|
||||||
let remoteStorage: RemoteStorageComponent | undefined = undefined;
|
const remoteStorage = this.resolveRemoteUrl(
|
||||||
|
remoteStorageSettings.remote_urls,
|
||||||
for (const remoteUrl of remoteStorageSettings.remote_urls) {
|
);
|
||||||
const item = this.metadata.item(remoteUrl);
|
|
||||||
const candidate = item.RemoteStorage();
|
|
||||||
|
|
||||||
if (
|
|
||||||
remoteStorage === undefined ||
|
|
||||||
candidate.download_speed_weight > remoteStorage.download_speed_weight
|
|
||||||
) {
|
|
||||||
remoteStorage = candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remoteStorage === undefined) {
|
|
||||||
throw new Error("No suitable production remote storage found");
|
|
||||||
}
|
|
||||||
|
|
||||||
const configPath = `${remoteStorage.url}${remoteStorage.relative_path}/config.yaml`;
|
const configPath = `${remoteStorage.url}${remoteStorage.relative_path}/config.yaml`;
|
||||||
const config = await fetch(configPath)
|
const config = await fetch(configPath)
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
@@ -344,4 +355,23 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
|
|
||||||
return { backgrounds };
|
return { backgrounds };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Cache()
|
||||||
|
async currencyIcon(id: string) {
|
||||||
|
const item = this.metadata.item(`CurrencyEntity.${id}`);
|
||||||
|
const stuffUI = item.StuffUI("UIComponent");
|
||||||
|
const config = this.resolveClientConfig();
|
||||||
|
const metroResources = config.MetroResources();
|
||||||
|
const remoteStorage = this.resolveRemoteUrl(
|
||||||
|
metroResources.remote_storage!.remote_urls,
|
||||||
|
);
|
||||||
|
|
||||||
|
const iconPathHack = stuffUI.icon.replace("\n", "");
|
||||||
|
|
||||||
|
if (!stuffUI.icon.includes("\n")) {
|
||||||
|
throw new Error("Icon path doesn't include newline, can't apply hack");
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${remoteStorage.url}${remoteStorage.relative_path}${iconPathHack}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type { GetStaticPaths } from "astro";
|
||||||
|
import { api } from "../../../core/api/dynamic";
|
||||||
|
import { imageProxy } from "../../../core/api/imageProxy";
|
||||||
|
|
||||||
|
export const getStaticPaths = (async () => {
|
||||||
|
const group = api.metadata.group("CurrencyEntity");
|
||||||
|
return group.map(({ name }) => ({ params: { id: name } }));
|
||||||
|
}) satisfies GetStaticPaths;
|
||||||
|
|
||||||
|
export async function GET({ params }: { params: { id: string } }) {
|
||||||
|
const icon = await api.currencyIcon(params.id);
|
||||||
|
return await imageProxy(icon);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user