mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: hot garbage
This commit is contained in:
1 parent
ae5e38139b
commit
d0170d53e4
19 files changed
+136
-210
No files matched your search
@@ -42,6 +42,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"googleapis": "^144.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"protobufjs": "^7.5.2",
|
||||
"quicklime": "^1.2.0",
|
||||
@@ -49,6 +50,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^22.14.1",
|
||||
"prettier": "^3.5.3",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"dependencies": {
|
||||
"@blitzkit/closed": "workspace:*",
|
||||
"@octokit/plugin-throttling": "^8.2.0",
|
||||
"@octokit/rest": "^20.1.1",
|
||||
"@prisma/client": "^6.6.0",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export function entityName(entity: string) {
|
||||
const [_, ...name] = entity.split('.');
|
||||
return name.join('.');
|
||||
}
|
||||
@@ -2,6 +2,7 @@ export * from './canSplash';
|
||||
export * from './clans';
|
||||
export * from './classes';
|
||||
export * from './constants';
|
||||
export * from './entityName';
|
||||
export * from './equipmentPrice';
|
||||
export * from './fetchBlitz';
|
||||
export * from './fetchGlossary';
|
||||
@@ -18,6 +19,7 @@ export * from './getTankStats';
|
||||
export * from './idToRegion';
|
||||
export * from './isOnGoingRatingSeason';
|
||||
export * from './isValidBlitzId';
|
||||
export * from './metadataAccessor';
|
||||
export * from './nations';
|
||||
export * from './normalizeExtra';
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1037,6 +1037,7 @@
|
||||
},
|
||||
|
||||
"meta": {
|
||||
"compensation": "Compensation",
|
||||
"free": "Free",
|
||||
"back": "Back",
|
||||
"compare": "Compare",
|
||||
@@ -1051,7 +1052,7 @@
|
||||
"class": "Class",
|
||||
"tier": "Tier",
|
||||
"type": "Type",
|
||||
"dev_id": "DEV: ID",
|
||||
"id": "ID",
|
||||
"purchase": "Purchase price",
|
||||
"restoration": "Restoration 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 { Var } from '../../core/radix/var';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { App } from '../../stores/app';
|
||||
import { TankopediaEphemeral_ue } from '../../stores/tankopediaEphemeral_ue';
|
||||
import { classIcons_ue } from '../ClassIcon';
|
||||
import { Currency } from '../Currency';
|
||||
import { Listing } from '../Tankopedia/MetaSection/components/Listing';
|
||||
|
||||
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() {
|
||||
const developerMode = App.useDeferred((state) => state.developerMode, false);
|
||||
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 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 { locale, strings } = useLocale();
|
||||
const { locale, strings, gameStrings } = useLocale();
|
||||
const reward = set.tank_set_rewards.find(
|
||||
(reward) =>
|
||||
reward.tank_set_reward_on_level &&
|
||||
@@ -84,9 +87,11 @@ export function MetaSection() {
|
||||
{strings.common.tank_class[tank.tank_class]}
|
||||
</Flex>
|
||||
</Listing>
|
||||
|
||||
<Listing label={strings.website.tools.tankopedia.meta.tier}>
|
||||
{TIER_ROMAN_NUMERALS[tank.tier]}
|
||||
</Listing>
|
||||
|
||||
<Listing
|
||||
label={strings.website.tools.tankopedia.meta.type}
|
||||
color={
|
||||
@@ -99,71 +104,19 @@ export function MetaSection() {
|
||||
>
|
||||
{strings.common.tree_type[tank.tank_type]}
|
||||
</Listing>
|
||||
|
||||
<Listing label={strings.website.tools.tankopedia.meta.id}>
|
||||
<Code variant="ghost">
|
||||
{entity.replace('TankEntity.', '')}
|
||||
</Code>
|
||||
</Listing>
|
||||
</Flex>
|
||||
|
||||
<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}>
|
||||
{set.tank_set_ui!.tank_set_displayed_name}
|
||||
{gameStrings[set.tank_set_ui!.tank_set_displayed_name]}
|
||||
</Listing>
|
||||
|
||||
<Listing label={strings.website.tools.tankopedia.meta.price}>
|
||||
<Flex align="center" gap="1">
|
||||
{reward.tank_set_reward_price === undefined ? (
|
||||
@@ -183,6 +136,16 @@ export function MetaSection() {
|
||||
)}
|
||||
</Flex>
|
||||
</Listing>
|
||||
|
||||
{compensation.compensation?.currency_reward && (
|
||||
<Listing
|
||||
label={strings.website.tools.tankopedia.meta.compensation}
|
||||
>
|
||||
<Currency
|
||||
reward={compensation.compensation.currency_reward}
|
||||
/>
|
||||
</Listing>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export function TestComponent({ data }: { data: any }) {
|
||||
return 'TestComponent';
|
||||
}
|
||||
@@ -1,24 +1,26 @@
|
||||
import { type BlitzKitStrings } from '@blitzkit/i18n';
|
||||
import { createContext, useContext, type ReactNode } from 'react';
|
||||
|
||||
const LocaleContext = createContext<{
|
||||
interface LocaleContextContent {
|
||||
locale: string;
|
||||
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 {
|
||||
locale: string;
|
||||
strings: BlitzKitStrings;
|
||||
data: LocaleContextData;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function LocaleProvider({
|
||||
locale,
|
||||
strings,
|
||||
children,
|
||||
}: LocaleProviderProps) {
|
||||
export function LocaleProvider({ data, children }: LocaleProviderProps) {
|
||||
return (
|
||||
<LocaleContext.Provider value={{ locale, strings }}>
|
||||
<LocaleContext.Provider value={{ gameStrings: {}, ...data }}>
|
||||
{children}
|
||||
</LocaleContext.Provider>
|
||||
);
|
||||
@@ -31,11 +33,9 @@ export function useLocale() {
|
||||
throw new Error('useLocale must be used within a LocaleProvider');
|
||||
}
|
||||
|
||||
const { locale, strings } = context;
|
||||
|
||||
return { locale, strings };
|
||||
return context;
|
||||
}
|
||||
|
||||
export interface LocaleAcceptorProps {
|
||||
localeContext: { locale: string; strings: BlitzKitStrings };
|
||||
localeContext: LocaleContextContent;
|
||||
}
|
||||
@@ -30,7 +30,6 @@ export const getStaticPaths = (() => {
|
||||
return locales.supported.map(({ locale, blitz }) => {
|
||||
return {
|
||||
params: { locale: locale === locales.default ? undefined : locale },
|
||||
|
||||
props: {
|
||||
locale,
|
||||
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 { MetaSection } from 'packages/website/src/components/Tankopedia_ue/MetaSection';
|
||||
import {
|
||||
@@ -11,25 +8,14 @@ import { App } from 'packages/website/src/stores/app';
|
||||
import { TankopediaEphemeral_ue } from 'packages/website/src/stores/tankopediaEphemeral_ue';
|
||||
|
||||
interface PageProps extends LocaleAcceptorProps {
|
||||
tank: TankCatalogComponent;
|
||||
compensation: CompensationComponent;
|
||||
set: TankSetCatalogComponent;
|
||||
catalogueId: string;
|
||||
tankContext: TankopediaEphemeral_ue;
|
||||
}
|
||||
|
||||
export function Page({
|
||||
tank,
|
||||
compensation,
|
||||
set,
|
||||
localeContext,
|
||||
catalogueId,
|
||||
}: PageProps) {
|
||||
export function Page({ localeContext, tankContext }: PageProps) {
|
||||
return (
|
||||
<LocaleProvider {...localeContext}>
|
||||
<LocaleProvider data={localeContext}>
|
||||
<App.Provider>
|
||||
<TankopediaEphemeral_ue.Provider
|
||||
data={{ tank, compensation, set, catalogueId }}
|
||||
>
|
||||
<TankopediaEphemeral_ue.Provider data={tankContext}>
|
||||
<PageWrapper p="0" maxWidth="unset" color="purple" gap="9" pb="9">
|
||||
<MetaSection />
|
||||
</PageWrapper>
|
||||
|
||||
@@ -8,21 +8,21 @@ import { slugify } from '@blitzkit/core';
|
||||
import locales from '@blitzkit/i18n/locales.json';
|
||||
import type { GetStaticPaths } from 'astro';
|
||||
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 BaseLayout from '../../../../layouts/BaseLayout.astro';
|
||||
import { getStaticPaths as parentGetStaticPaths } from '../index.astro';
|
||||
import { Page } from './_index';
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const tanks = await metadata
|
||||
.get('CatalogItemsGroupEntity.all_tanks')
|
||||
.then((components) =>
|
||||
components.get<CatalogItemsComponent>('catalogItemsGroupComponent'),
|
||||
);
|
||||
const tanks = await metadata.getComponent<CatalogItemsComponent>(
|
||||
'CatalogItemsGroupEntity.all_tanks',
|
||||
'catalogItemsGroupComponent',
|
||||
);
|
||||
const gameStringsDefault = await metadata.stringsSubset(locales.default, [
|
||||
'TankEntity',
|
||||
]);
|
||||
const parentPaths = parentGetStaticPaths();
|
||||
const gameStringsDefault = await metadata
|
||||
.strings(locales.default)
|
||||
.then((strings) => strings.subset(['TankEntity']));
|
||||
|
||||
return (
|
||||
await Promise.all(
|
||||
@@ -36,7 +36,7 @@ export const getStaticPaths = (async () => {
|
||||
return parentPaths.map((parent) => {
|
||||
return {
|
||||
params: { ...parent.params, slug },
|
||||
props: { ...parent.props, item },
|
||||
props: { ...parent.props, entity: item },
|
||||
};
|
||||
});
|
||||
}),
|
||||
@@ -44,24 +44,23 @@ export const getStaticPaths = (async () => {
|
||||
).flat();
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
const { item, locale, strings } = Astro.props;
|
||||
const set = await tankToSet(metadata, item);
|
||||
const tankComponents = await metadata.get(item);
|
||||
const tank = await tankComponents.get<TankCatalogComponent>(
|
||||
'tankCatalogComponent',
|
||||
);
|
||||
const compensation = await tankComponents.get<CompensationComponent>(
|
||||
const { entity, locale, strings, blitzLocale } = Astro.props;
|
||||
const set = await tankToSet(metadata, entity);
|
||||
const gameStrings = await metadata.stringsSubset(blitzLocale, [
|
||||
set.tank_set_ui!.tank_set_displayed_name,
|
||||
]);
|
||||
const tankEntity = await metadata.get(entity);
|
||||
const tank = await tankEntity.get<TankCatalogComponent>('tankCatalogComponent');
|
||||
const compensation = await tankEntity.get<CompensationComponent>(
|
||||
'compensationComponent',
|
||||
);
|
||||
const tankContext: TankopediaEphemeral_ue = { tank, set, compensation, entity };
|
||||
---
|
||||
|
||||
<BaseLayout {locale} {strings}>
|
||||
<Page
|
||||
client:load
|
||||
catalogueId={item}
|
||||
{set}
|
||||
{tank}
|
||||
{compensation}
|
||||
localeContext={{ locale, strings }}
|
||||
{tankContext}
|
||||
localeContext={{ locale, strings, gameStrings }}
|
||||
/>
|
||||
</BaseLayout>
|
||||
@@ -3,35 +3,41 @@ import { slugify } from '@blitzkit/core';
|
||||
import locales from '@blitzkit/i18n/locales.json';
|
||||
import type { CatalogItemsComponent } from '@protos/blitz_static_catalog_items_group_component';
|
||||
import { TankCatalogComponent } from '@protos/blitz_static_tank_component';
|
||||
import { TestComponent } from 'packages/website/src/components/TestComponent';
|
||||
import { metadata } from '../../../core/blitz/metadata';
|
||||
|
||||
export { getStaticPaths } from '../index.astro';
|
||||
|
||||
const { blitzLocale } = Astro.props;
|
||||
const allTanks = await metadata
|
||||
.get('CatalogItemsGroupEntity.all_tanks')
|
||||
.then((components) =>
|
||||
components.get<CatalogItemsComponent>('catalogItemsGroupComponent'),
|
||||
);
|
||||
const gameStrings = await metadata
|
||||
.strings(blitzLocale)
|
||||
.then((strings) => strings.subset(['TankEntity']));
|
||||
const gameStringsDefault = await metadata
|
||||
.strings(locales.default)
|
||||
.then((strings) => strings.subset(['TankEntity']));
|
||||
const allTanks = await metadata.getComponent<CatalogItemsComponent>(
|
||||
'CatalogItemsGroupEntity.all_tanks',
|
||||
'catalogItemsGroupComponent',
|
||||
);
|
||||
const gameStrings = await metadata.stringsSubset(blitzLocale, ['TankEntity']);
|
||||
const gameStringsDefault = await metadata.stringsSubset(locales.default, [
|
||||
'TankEntity',
|
||||
]);
|
||||
const tanks: { tank: TankCatalogComponent; slug: string; id: string }[] = [];
|
||||
|
||||
for (const tankComponentId of allTanks.catalog_items) {
|
||||
const tank = await metadata.get(tankComponentId).then((components) => {
|
||||
return components.get<TankCatalogComponent>('tankCatalogComponent');
|
||||
});
|
||||
const nameEn = gameStringsDefault[tank.name!.value];
|
||||
const tank = await metadata.getComponent<TankCatalogComponent>(
|
||||
tankComponentId,
|
||||
'tankCatalogComponent',
|
||||
);
|
||||
|
||||
if (tank.name === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const nameEn = gameStringsDefault[tank.name.value];
|
||||
const slug = slugify(nameEn);
|
||||
|
||||
tanks.push({ tank, slug, id: tankComponentId });
|
||||
}
|
||||
---
|
||||
|
||||
<TestComponent client:load data={metadata} />
|
||||
|
||||
{
|
||||
tanks.map(({ tank, slug, id }) => {
|
||||
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 { createContextualStore } from '../core/zustand/createContextualStore';
|
||||
|
||||
interface TankopediaEphemeral_ue {
|
||||
export interface TankopediaEphemeral_ue {
|
||||
tank: TankCatalogComponent;
|
||||
compensation: CompensationComponent;
|
||||
set: TankSetCatalogComponent;
|
||||
catalogueId: string;
|
||||
entity: string;
|
||||
}
|
||||
|
||||
export const TankopediaEphemeral_ue = createContextualStore(
|
||||
|
||||
Submodule submodules/blitzkit-closed updated: 793d8c9f4e...accc9ee26b.
Reference in new issue
Block a user