feat: hot garbage

This commit is contained in:
TrèsAbhi committed 2025-05-26 14:23:00 -05:00
1 parent ae5e38139b
commit d0170d53e4
19 files changed
+136 -210

No files matched your search

BIN
View File
Binary file not shown.
+2
View File
@@ -42,6 +42,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"googleapis": "^144.0.0", "googleapis": "^144.0.0",
"js-yaml": "^4.1.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"protobufjs": "^7.5.2", "protobufjs": "^7.5.2",
"quicklime": "^1.2.0", "quicklime": "^1.2.0",
@@ -49,6 +50,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "latest",
"@types/js-yaml": "^4.0.9",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^22.14.1", "@types/node": "^22.14.1",
"prettier": "^3.5.3", "prettier": "^3.5.3",
+1
View File
@@ -4,6 +4,7 @@
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"dependencies": { "dependencies": {
"@blitzkit/closed": "workspace:*",
"@octokit/plugin-throttling": "^8.2.0", "@octokit/plugin-throttling": "^8.2.0",
"@octokit/rest": "^20.1.1", "@octokit/rest": "^20.1.1",
"@prisma/client": "^6.6.0", "@prisma/client": "^6.6.0",
+4
View File
@@ -0,0 +1,4 @@
export function entityName(entity: string) {
const [_, ...name] = entity.split('.');
return name.join('.');
}
+2
View File
@@ -2,6 +2,7 @@ export * from './canSplash';
export * from './clans'; export * from './clans';
export * from './classes'; export * from './classes';
export * from './constants'; export * from './constants';
export * from './entityName';
export * from './equipmentPrice'; export * from './equipmentPrice';
export * from './fetchBlitz'; export * from './fetchBlitz';
export * from './fetchGlossary'; export * from './fetchGlossary';
@@ -18,6 +19,7 @@ export * from './getTankStats';
export * from './idToRegion'; export * from './idToRegion';
export * from './isOnGoingRatingSeason'; export * from './isOnGoingRatingSeason';
export * from './isValidBlitzId'; export * from './isValidBlitzId';
export * from './metadataAccessor';
export * from './nations'; export * from './nations';
export * from './normalizeExtra'; export * from './normalizeExtra';
export * from './normalizeIds'; export * from './normalizeIds';
@@ -0,0 +1,10 @@
import { Metadata } from '@protos/blitz_metadata';
export class MetadataAccessor {
constructor() {}
loadMetadata(metadata: Metadata) {
for (const catalogItem of metadata.catalog_items) {
}
}
}
+2 -1
View File
@@ -1037,6 +1037,7 @@
}, },
"meta": { "meta": {
"compensation": "Compensation",
"free": "Free", "free": "Free",
"back": "Back", "back": "Back",
"compare": "Compare", "compare": "Compare",
@@ -1051,7 +1052,7 @@
"class": "Class", "class": "Class",
"tier": "Tier", "tier": "Tier",
"type": "Type", "type": "Type",
"dev_id": "DEV: ID", "id": "ID",
"purchase": "Purchase price", "purchase": "Purchase price",
"restoration": "Restoration price", "restoration": "Restoration price",
"sale": "Sale price", "sale": "Sale price",
@@ -0,0 +1,23 @@
import type { RewardCurrency } from '@protos/blitz_static_reward_currency';
import { Flex, Text, type TextProps } from '@radix-ui/themes';
import { entityName } from 'packages/core/src';
import { useLocale } from '../hooks/useLocale';
type CurrencyProps = TextProps & {
reward: RewardCurrency;
};
export function Currency({ reward, ...props }: CurrencyProps) {
const { locale } = useLocale();
return (
<Text {...props}>
<Flex align="center" gap="1">
{reward.amount.toLocaleString(locale)}
<img
src={`/api/currencies/${entityName(reward.currency_catalog_id)}.webp`}
/>
</Flex>
</Text>
);
}
@@ -3,9 +3,9 @@ import { Box, Code, Flex } from '@radix-ui/themes';
import { asset, TIER_ROMAN_NUMERALS } from 'packages/core/src'; import { asset, TIER_ROMAN_NUMERALS } from 'packages/core/src';
import { Var } from '../../core/radix/var'; import { Var } from '../../core/radix/var';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
import { App } from '../../stores/app';
import { TankopediaEphemeral_ue } from '../../stores/tankopediaEphemeral_ue'; import { TankopediaEphemeral_ue } from '../../stores/tankopediaEphemeral_ue';
import { classIcons_ue } from '../ClassIcon'; import { classIcons_ue } from '../ClassIcon';
import { Currency } from '../Currency';
import { Listing } from '../Tankopedia/MetaSection/components/Listing'; import { Listing } from '../Tankopedia/MetaSection/components/Listing';
const NATIONAL_BANNER_POSITION_OVERRIDES: Partial<Record<Nation, string>> = { const NATIONAL_BANNER_POSITION_OVERRIDES: Partial<Record<Nation, string>> = {
@@ -15,12 +15,15 @@ const NATIONAL_BANNER_POSITION_OVERRIDES: Partial<Record<Nation, string>> = {
}; };
export function MetaSection() { export function MetaSection() {
const developerMode = App.useDeferred((state) => state.developerMode, false);
const tank = TankopediaEphemeral_ue.use((state) => state.tank); const tank = TankopediaEphemeral_ue.use((state) => state.tank);
const entity = TankopediaEphemeral_ue.use((state) => state.entity);
const set = TankopediaEphemeral_ue.use((state) => state.set); const set = TankopediaEphemeral_ue.use((state) => state.set);
const catalogueId = TankopediaEphemeral_ue.use((state) => state.catalogueId); const compensation = TankopediaEphemeral_ue.use(
(state) => state.compensation,
);
const catalogueId = TankopediaEphemeral_ue.use((state) => state.entity);
const ClassIcon = classIcons_ue[tank.tank_class]; const ClassIcon = classIcons_ue[tank.tank_class];
const { locale, strings } = useLocale(); const { locale, strings, gameStrings } = useLocale();
const reward = set.tank_set_rewards.find( const reward = set.tank_set_rewards.find(
(reward) => (reward) =>
reward.tank_set_reward_on_level && reward.tank_set_reward_on_level &&
@@ -84,9 +87,11 @@ export function MetaSection() {
{strings.common.tank_class[tank.tank_class]} {strings.common.tank_class[tank.tank_class]}
</Flex> </Flex>
</Listing> </Listing>
<Listing label={strings.website.tools.tankopedia.meta.tier}> <Listing label={strings.website.tools.tankopedia.meta.tier}>
{TIER_ROMAN_NUMERALS[tank.tier]} {TIER_ROMAN_NUMERALS[tank.tier]}
</Listing> </Listing>
<Listing <Listing
label={strings.website.tools.tankopedia.meta.type} label={strings.website.tools.tankopedia.meta.type}
color={ color={
@@ -99,71 +104,19 @@ export function MetaSection() {
> >
{strings.common.tree_type[tank.tank_type]} {strings.common.tree_type[tank.tank_type]}
</Listing> </Listing>
<Listing label={strings.website.tools.tankopedia.meta.id}>
<Code variant="ghost">
{entity.replace('TankEntity.', '')}
</Code>
</Listing>
</Flex> </Flex>
<Flex direction="column" width="100%" gap="2"> <Flex direction="column" width="100%" gap="2">
{developerMode && (
<Listing
label={strings.website.tools.tankopedia.meta.dev_id}
>
<Code>TANK_ID</Code>
</Listing>
)}
{tank.tank_type === TankType.TANK_TYPE_PREMIUM && (
<Listing
label={strings.website.tools.tankopedia.meta.purchase}
>
<Flex align="center" gap="1">
{(9999999999).toLocaleString(locale)}
<img
style={{ width: '1em', height: '1em' }}
alt="gold"
src={asset('icons/currencies/gold.webp')}
/>
</Flex>
</Listing>
)}
<Listing
label={
tank.tank_type === TankType.TANK_TYPE_COMMON
? strings.website.tools.tankopedia.meta.purchase
: strings.website.tools.tankopedia.meta.restoration
}
>
<Flex align="center" gap="1">
{(999999999).toLocaleString(locale)}
<img
style={{ width: '1em', height: '1em' }}
alt="TODO: REPLACE WITH CURRENCY NAME"
// src={asset(
// `icons/currencies/${
// protagonist.price.type === TankPriceType.GOLD
// ? 'gold'
// : 'silver'
// }.webp`,
// )}
/>
</Flex>
</Listing>
<Listing label={strings.website.tools.tankopedia.meta.sale}>
<Flex align="center" gap="1">
{(999999999).toLocaleString(locale)}
<img
style={{ width: '1em', height: '1em' }}
alt="TODO: REPLACE WITH CURRENCY NAME"
// src={asset(
// `icons/currencies/${
// protagonist.price.type === TankPriceType.GOLD
// ? 'gold'
// : 'silver'
// }.webp`,
// )}
/>
</Flex>
</Listing>
<Listing label={strings.website.tools.tankopedia.meta.set}> <Listing label={strings.website.tools.tankopedia.meta.set}>
{set.tank_set_ui!.tank_set_displayed_name} {gameStrings[set.tank_set_ui!.tank_set_displayed_name]}
</Listing> </Listing>
<Listing label={strings.website.tools.tankopedia.meta.price}> <Listing label={strings.website.tools.tankopedia.meta.price}>
<Flex align="center" gap="1"> <Flex align="center" gap="1">
{reward.tank_set_reward_price === undefined ? ( {reward.tank_set_reward_price === undefined ? (
@@ -183,6 +136,16 @@ export function MetaSection() {
)} )}
</Flex> </Flex>
</Listing> </Listing>
{compensation.compensation?.currency_reward && (
<Listing
label={strings.website.tools.tankopedia.meta.compensation}
>
<Currency
reward={compensation.compensation.currency_reward}
/>
</Listing>
)}
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
@@ -0,0 +1,3 @@
export function TestComponent({ data }: { data: any }) {
return 'TestComponent';
}
+14 -14
View File
@@ -1,24 +1,26 @@
import { type BlitzKitStrings } from '@blitzkit/i18n'; import { type BlitzKitStrings } from '@blitzkit/i18n';
import { createContext, useContext, type ReactNode } from 'react'; import { createContext, useContext, type ReactNode } from 'react';
const LocaleContext = createContext<{ interface LocaleContextContent {
locale: string; locale: string;
strings: BlitzKitStrings; strings: BlitzKitStrings;
} | null>(null); gameStrings: Record<string, string>;
}
type LocaleContextData = Omit<LocaleContextContent, 'gameStrings'> & {
gameStrings?: Record<string, string>;
};
const LocaleContext = createContext<LocaleContextContent | null>(null);
interface LocaleProviderProps { interface LocaleProviderProps {
locale: string; data: LocaleContextData;
strings: BlitzKitStrings;
children: ReactNode; children: ReactNode;
} }
export function LocaleProvider({ export function LocaleProvider({ data, children }: LocaleProviderProps) {
locale,
strings,
children,
}: LocaleProviderProps) {
return ( return (
<LocaleContext.Provider value={{ locale, strings }}> <LocaleContext.Provider value={{ gameStrings: {}, ...data }}>
{children} {children}
</LocaleContext.Provider> </LocaleContext.Provider>
); );
@@ -31,11 +33,9 @@ export function useLocale() {
throw new Error('useLocale must be used within a LocaleProvider'); throw new Error('useLocale must be used within a LocaleProvider');
} }
const { locale, strings } = context; return context;
return { locale, strings };
} }
export interface LocaleAcceptorProps { export interface LocaleAcceptorProps {
localeContext: { locale: string; strings: BlitzKitStrings }; localeContext: LocaleContextContent;
} }
@@ -30,7 +30,6 @@ export const getStaticPaths = (() => {
return locales.supported.map(({ locale, blitz }) => { return locales.supported.map(({ locale, blitz }) => {
return { return {
params: { locale: locale === locales.default ? undefined : locale }, params: { locale: locale === locales.default ? undefined : locale },
props: { props: {
locale, locale,
strings: STRINGS[locale as SupportedLocale], strings: STRINGS[locale as SupportedLocale],
@@ -1,6 +1,3 @@
import type { CompensationComponent } from '@protos/blitz_static_compensation_component';
import { type TankCatalogComponent } from '@protos/blitz_static_tank_component';
import type { TankSetCatalogComponent } from '@protos/blitz_static_tank_set_component';
import { PageWrapper } from 'packages/website/src/components/PageWrapper'; import { PageWrapper } from 'packages/website/src/components/PageWrapper';
import { MetaSection } from 'packages/website/src/components/Tankopedia_ue/MetaSection'; import { MetaSection } from 'packages/website/src/components/Tankopedia_ue/MetaSection';
import { import {
@@ -11,25 +8,14 @@ import { App } from 'packages/website/src/stores/app';
import { TankopediaEphemeral_ue } from 'packages/website/src/stores/tankopediaEphemeral_ue'; import { TankopediaEphemeral_ue } from 'packages/website/src/stores/tankopediaEphemeral_ue';
interface PageProps extends LocaleAcceptorProps { interface PageProps extends LocaleAcceptorProps {
tank: TankCatalogComponent; tankContext: TankopediaEphemeral_ue;
compensation: CompensationComponent;
set: TankSetCatalogComponent;
catalogueId: string;
} }
export function Page({ export function Page({ localeContext, tankContext }: PageProps) {
tank,
compensation,
set,
localeContext,
catalogueId,
}: PageProps) {
return ( return (
<LocaleProvider {...localeContext}> <LocaleProvider data={localeContext}>
<App.Provider> <App.Provider>
<TankopediaEphemeral_ue.Provider <TankopediaEphemeral_ue.Provider data={tankContext}>
data={{ tank, compensation, set, catalogueId }}
>
<PageWrapper p="0" maxWidth="unset" color="purple" gap="9" pb="9"> <PageWrapper p="0" maxWidth="unset" color="purple" gap="9" pb="9">
<MetaSection /> <MetaSection />
</PageWrapper> </PageWrapper>
@@ -8,21 +8,21 @@ import { slugify } from '@blitzkit/core';
import locales from '@blitzkit/i18n/locales.json'; import locales from '@blitzkit/i18n/locales.json';
import type { GetStaticPaths } from 'astro'; import type { GetStaticPaths } from 'astro';
import { tankToSet } from 'packages/website/src/core/blitz/tankToSet'; import { tankToSet } from 'packages/website/src/core/blitz/tankToSet';
import type { TankopediaEphemeral_ue } from 'packages/website/src/stores/tankopediaEphemeral_ue';
import { metadata } from '../../../../core/blitz/metadata'; import { metadata } from '../../../../core/blitz/metadata';
import BaseLayout from '../../../../layouts/BaseLayout.astro'; import BaseLayout from '../../../../layouts/BaseLayout.astro';
import { getStaticPaths as parentGetStaticPaths } from '../index.astro'; import { getStaticPaths as parentGetStaticPaths } from '../index.astro';
import { Page } from './_index'; import { Page } from './_index';
export const getStaticPaths = (async () => { export const getStaticPaths = (async () => {
const tanks = await metadata const tanks = await metadata.getComponent<CatalogItemsComponent>(
.get('CatalogItemsGroupEntity.all_tanks') 'CatalogItemsGroupEntity.all_tanks',
.then((components) => 'catalogItemsGroupComponent',
components.get<CatalogItemsComponent>('catalogItemsGroupComponent'), );
); const gameStringsDefault = await metadata.stringsSubset(locales.default, [
'TankEntity',
]);
const parentPaths = parentGetStaticPaths(); const parentPaths = parentGetStaticPaths();
const gameStringsDefault = await metadata
.strings(locales.default)
.then((strings) => strings.subset(['TankEntity']));
return ( return (
await Promise.all( await Promise.all(
@@ -36,7 +36,7 @@ export const getStaticPaths = (async () => {
return parentPaths.map((parent) => { return parentPaths.map((parent) => {
return { return {
params: { ...parent.params, slug }, params: { ...parent.params, slug },
props: { ...parent.props, item }, props: { ...parent.props, entity: item },
}; };
}); });
}), }),
@@ -44,24 +44,23 @@ export const getStaticPaths = (async () => {
).flat(); ).flat();
}) satisfies GetStaticPaths; }) satisfies GetStaticPaths;
const { item, locale, strings } = Astro.props; const { entity, locale, strings, blitzLocale } = Astro.props;
const set = await tankToSet(metadata, item); const set = await tankToSet(metadata, entity);
const tankComponents = await metadata.get(item); const gameStrings = await metadata.stringsSubset(blitzLocale, [
const tank = await tankComponents.get<TankCatalogComponent>( set.tank_set_ui!.tank_set_displayed_name,
'tankCatalogComponent', ]);
); const tankEntity = await metadata.get(entity);
const compensation = await tankComponents.get<CompensationComponent>( const tank = await tankEntity.get<TankCatalogComponent>('tankCatalogComponent');
const compensation = await tankEntity.get<CompensationComponent>(
'compensationComponent', 'compensationComponent',
); );
const tankContext: TankopediaEphemeral_ue = { tank, set, compensation, entity };
--- ---
<BaseLayout {locale} {strings}> <BaseLayout {locale} {strings}>
<Page <Page
client:load client:load
catalogueId={item} {tankContext}
{set} localeContext={{ locale, strings, gameStrings }}
{tank}
{compensation}
localeContext={{ locale, strings }}
/> />
</BaseLayout> </BaseLayout>
@@ -3,35 +3,41 @@ import { slugify } from '@blitzkit/core';
import locales from '@blitzkit/i18n/locales.json'; import locales from '@blitzkit/i18n/locales.json';
import type { CatalogItemsComponent } from '@protos/blitz_static_catalog_items_group_component'; import type { CatalogItemsComponent } from '@protos/blitz_static_catalog_items_group_component';
import { TankCatalogComponent } from '@protos/blitz_static_tank_component'; import { TankCatalogComponent } from '@protos/blitz_static_tank_component';
import { TestComponent } from 'packages/website/src/components/TestComponent';
import { metadata } from '../../../core/blitz/metadata'; import { metadata } from '../../../core/blitz/metadata';
export { getStaticPaths } from '../index.astro'; export { getStaticPaths } from '../index.astro';
const { blitzLocale } = Astro.props; const { blitzLocale } = Astro.props;
const allTanks = await metadata const allTanks = await metadata.getComponent<CatalogItemsComponent>(
.get('CatalogItemsGroupEntity.all_tanks') 'CatalogItemsGroupEntity.all_tanks',
.then((components) => 'catalogItemsGroupComponent',
components.get<CatalogItemsComponent>('catalogItemsGroupComponent'), );
); const gameStrings = await metadata.stringsSubset(blitzLocale, ['TankEntity']);
const gameStrings = await metadata const gameStringsDefault = await metadata.stringsSubset(locales.default, [
.strings(blitzLocale) 'TankEntity',
.then((strings) => strings.subset(['TankEntity'])); ]);
const gameStringsDefault = await metadata
.strings(locales.default)
.then((strings) => strings.subset(['TankEntity']));
const tanks: { tank: TankCatalogComponent; slug: string; id: string }[] = []; const tanks: { tank: TankCatalogComponent; slug: string; id: string }[] = [];
for (const tankComponentId of allTanks.catalog_items) { for (const tankComponentId of allTanks.catalog_items) {
const tank = await metadata.get(tankComponentId).then((components) => { const tank = await metadata.getComponent<TankCatalogComponent>(
return components.get<TankCatalogComponent>('tankCatalogComponent'); tankComponentId,
}); 'tankCatalogComponent',
const nameEn = gameStringsDefault[tank.name!.value]; );
if (tank.name === undefined) {
continue;
}
const nameEn = gameStringsDefault[tank.name.value];
const slug = slugify(nameEn); const slug = slugify(nameEn);
tanks.push({ tank, slug, id: tankComponentId }); tanks.push({ tank, slug, id: tankComponentId });
} }
--- ---
<TestComponent client:load data={metadata} />
{ {
tanks.map(({ tank, slug, id }) => { tanks.map(({ tank, slug, id }) => {
return ( return (
@@ -1,43 +0,0 @@
import { TankSandbox } from '../../../../../components/Tankopedia/HeroSection/components/TankSandbox';
import { awaitableModelDefinitions } from '../../../../../core/awaitables/modelDefinitions';
import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions';
import { awaitableTankDefinitions } from '../../../../../core/awaitables/tankDefinitions';
import { LocaleProvider } from '../../../../../hooks/useLocale';
import { App } from '../../../../../stores/app';
import { Duel } from '../../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
const [provisionDefinitions, tankDefinitions, modelDefinitions] =
await Promise.all([
awaitableProvisionDefinitions,
awaitableTankDefinitions,
awaitableModelDefinitions,
]);
interface PageProps {
id: number;
}
export function Page({ id }: PageProps) {
const tank = tankDefinitions.tanks[id];
const model = modelDefinitions.models[id];
return (
<LocaleProvider locale="en">
<App.Provider>
<TankopediaPersistent.Provider>
<Duel.Provider data={{ provisionDefinitions, model, tank }}>
<TankopediaEphemeral.Provider data={model}>
<Content />
</TankopediaEphemeral.Provider>
</Duel.Provider>
</TankopediaPersistent.Provider>
</App.Provider>
</LocaleProvider>
);
}
function Content() {
return <TankSandbox naked thicknessRange={{ value: 0 }} />;
}
@@ -1,30 +0,0 @@
---
import { assertSecret } from '@blitzkit/core';
import type { GetStaticPaths } from 'astro';
import { getStaticPaths as tankopediaGetStaticPaths } from '../../../../[...locale]/tools/tankopedia/[id]/index.astro';
import { Page } from './_index';
export const getStaticPaths = (async () => {
if (assertSecret(import.meta.env.MODE) === 'development') {
return await tankopediaGetStaticPaths();
} else {
return [];
}
}) satisfies GetStaticPaths;
const { id } = Astro.params;
---
<html>
<head>
<style is:global>
body {
margin: 0;
}
</style>
</head>
<body>
<Page {id} client:load />
</body>
</html>
@@ -5,11 +5,11 @@ import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware'; import { subscribeWithSelector } from 'zustand/middleware';
import { createContextualStore } from '../core/zustand/createContextualStore'; import { createContextualStore } from '../core/zustand/createContextualStore';
interface TankopediaEphemeral_ue { export interface TankopediaEphemeral_ue {
tank: TankCatalogComponent; tank: TankCatalogComponent;
compensation: CompensationComponent; compensation: CompensationComponent;
set: TankSetCatalogComponent; set: TankSetCatalogComponent;
catalogueId: string; entity: string;
} }
export const TankopediaEphemeral_ue = createContextualStore( export const TankopediaEphemeral_ue = createContextualStore(