diff --git a/packages/website-ue/src/components/Footer.astro b/packages/website-ue/src/components/Footer.astro index 1ed27832c..d659b0e49 100644 --- a/packages/website-ue/src/components/Footer.astro +++ b/packages/website-ue/src/components/Footer.astro @@ -3,6 +3,7 @@ display: flex; justify-content: center; + margin-top: calc(var(--space-4) * -1); padding: var(--space-6) var(--space-4); background-color: var(--gray-3); diff --git a/packages/website-ue/src/core/api/abstract.ts b/packages/website-ue/src/core/api/abstract.ts index 550873a87..cebdfd64b 100644 --- a/packages/website-ue/src/core/api/abstract.ts +++ b/packages/website-ue/src/core/api/abstract.ts @@ -1,20 +1,32 @@ import type { CatalogItemAccessor } from "@blitzkit/closed"; import type { Strings } from "@blitzkit/i18n"; import locales from "@blitzkit/i18n/locales.json"; +import type { Avatar } from "../../protos/avatar"; +import type { Avatars } from "../../protos/avatars"; import type { Tank } from "../../protos/tank"; import type { TankList } from "../../protos/tank_list"; import type { Tanks } from "../../protos/tanks"; export abstract class AbstractAPI { - abstract tanks(): Promise; + private _tanksCache: Tanks | undefined; + protected abstract _tanks(): Promise; + async tanks() { + if (this._tanksCache === undefined) { + this._tanksCache = await this._tanks(); + } - abstract tankList(): Promise; + return this._tanksCache; + } - abstract _strings(locale: string): Promise; - abstract _gameStrings( - locale: string, - prefix: string - ): Promise>; + private _tankListCache: TankList | undefined; + protected abstract _tankList(): Promise; + async tankList() { + if (this._tankListCache === undefined) { + this._tankListCache = await this._tankList(); + } + + return this._tankListCache; + } private assertLocale(locale: string) { const supported = locales.supported.find( @@ -27,19 +39,73 @@ export abstract class AbstractAPI { return supported; } + + private _stringsCache: Record = {}; + protected abstract _strings(locale: string): Promise; async strings(locale: string) { this.assertLocale(locale); - return await this._strings(locale); + + if (this._stringsCache[locale] === undefined) { + this._stringsCache[locale] = await this._strings(locale); + } + + return this._stringsCache[locale]; } + + private _gameStringsCache: Record< + string, + Record> + > = {}; + protected abstract _gameStrings( + locale: string, + prefix: string + ): Promise>; async gameStrings(locale: string, prefix: string) { this.assertLocale(locale); - return await this._gameStrings(locale, prefix); + + if (this._gameStringsCache[locale] === undefined) { + this._gameStringsCache[locale] = {}; + } + + if (this._gameStringsCache[locale][prefix] === undefined) { + this._gameStringsCache[locale][prefix] = await this._gameStrings( + locale, + prefix + ); + } + + return this._gameStringsCache[locale][prefix]; } - abstract tank(id: string): Promise; + private _tankCache: Record = {}; + protected abstract _tank(id: string): Promise; + async tank(id: string) { + if (this._tankCache[id] === undefined) { + this._tankCache[id] = await this._tank(id); + } - abstract avatars(): Promise; - abstract avatar(id: string): Promise; + return this._tankCache[id]; + } + + private _avatarsCache: Avatars | undefined; + protected abstract _avatars(): Promise; + async avatars() { + if (this._avatarsCache === undefined) { + this._avatarsCache = await this._avatars(); + } + + return this._avatarsCache; + } + + private _avatarCache: Record = {}; + protected abstract _avatar(id: string): Promise; + async avatar(id: string) { + if (this._avatarCache[id] === undefined) { + this._avatarCache[id] = await this._avatar(id); + } + + return this._avatarCache[id]; + } abstract backgrounds(): Promise; abstract background(id: string): Promise; diff --git a/packages/website-ue/src/core/api/dynamic.ts b/packages/website-ue/src/core/api/dynamic.ts index 71ac53ebe..b119f3923 100644 --- a/packages/website-ue/src/core/api/dynamic.ts +++ b/packages/website-ue/src/core/api/dynamic.ts @@ -20,3 +20,5 @@ if (typeof window === "undefined") { } export const api = dynamicAPI; + + diff --git a/packages/website-ue/src/core/api/server.ts b/packages/website-ue/src/core/api/server.ts index 3836ca856..604ea96d8 100644 --- a/packages/website-ue/src/core/api/server.ts +++ b/packages/website-ue/src/core/api/server.ts @@ -5,8 +5,9 @@ import locales from "@blitzkit/i18n/locales.json"; import type { RemoteStorageComponent } from "@protos/blitz_static_remote_storage_component"; import { merge } from "lodash-es"; import { parse } from "yaml"; +import type { Avatar } from "../../protos/avatar"; import type { Tank } from "../../protos/tank"; -import type { TankList, TankListEntry } from "../../protos/tank_list"; +import type { TankListEntry } from "../../protos/tank_list"; import type { Tanks } from "../../protos/tanks"; import { AbstractAPI } from "./abstract"; @@ -27,7 +28,7 @@ export class ServerAPI extends AbstractAPI { super(); } - async _gameStrings(locale: string, prefix: string) { + protected async _gameStrings(locale: string, prefix: string) { const group = this.metadata.group("ClientConfigsEntity"); if (group.length !== 1) { @@ -107,7 +108,7 @@ export class ServerAPI extends AbstractAPI { return filtered; } - async _strings(locale: string) { + protected async _strings(locale: string) { const localized = (await globbedStrings[ `../../../../i18n/strings/${locale}.json` ]()) as DeepPartial; @@ -119,35 +120,30 @@ export class ServerAPI extends AbstractAPI { return strings; } - _tankList: TankList | undefined; - async tankList() { - if (this._tankList === undefined) { - const group = this.metadata.group("TankEntity"); - const list: TankListEntry[] = []; - const strings = await this.gameStrings("en", "TankEntity"); + protected async _tankList() { + const group = this.metadata.group("TankEntity"); + const list: TankListEntry[] = []; + const strings = await this.gameStrings("en", "TankEntity"); - for (const item of group) { - const tankCatalog = item.TankCatalog(); + for (const item of group) { + const tankCatalog = item.TankCatalog(); - if (!tankCatalog.name) continue; + if (!tankCatalog.name) continue; - const name = strings[tankCatalog.name.value]; + const name = strings[tankCatalog.name.value]; - if (!name) continue; + if (!name) continue; - const id = item.name; - const slug = sluggify(name); + const id = item.name; + const slug = sluggify(name); - list.push({ id, slug }); - } - - this._tankList = { list }; + list.push({ id, slug }); } - return this._tankList; + return { list }; } - async tanks() { + protected async _tanks() { const tankList = await this.tankList(); const data: Tanks = { tanks: {} }; @@ -158,7 +154,7 @@ export class ServerAPI extends AbstractAPI { return data; } - async tank(id: string) { + protected async _tank(id: string) { const tankList = await this.tankList(); const tankListEntry = tankList.list.find((tank) => tank.id === id); @@ -174,12 +170,26 @@ export class ServerAPI extends AbstractAPI { return { tank, compensation, slug } satisfies Tank; } - async avatars() { - return this.metadata.group("ProfileAvatarEntity"); + async _avatars() { + const avatars: Avatar[] = []; + + for (const item of this.metadata.group("ProfileAvatarEntity")) { + avatars.push(await this.avatar(item.name)); + } + + return { avatars }; } - async avatar(id: string) { - return this.metadata.item(`ProfileAvatarEntity.${id}`); + async _avatar(id: string) { + const avatar = this.metadata.item(`ProfileAvatarEntity.${id}`); + const name = avatar.name; + const stuff_ui = avatar.StuffUI("UIComponent"); + const profile_avatar = avatar.ProfileAvatar(); + const sellable = avatar.components.sellableComponent + ? avatar.Sellable() + : undefined; + + return { name, stuff_ui, profile_avatar, sellable }; } async backgrounds() { diff --git a/packages/website-ue/src/pages/[...locale]/avatars/index.astro b/packages/website-ue/src/pages/[...locale]/avatars/index.astro index bfa60dda6..36a7f4811 100644 --- a/packages/website-ue/src/pages/[...locale]/avatars/index.astro +++ b/packages/website-ue/src/pages/[...locale]/avatars/index.astro @@ -6,30 +6,31 @@ import BaseLayout from "../../../layouts/BaseLayout.astro"; export { getStaticPaths } from "../index.astro"; -const avatars = await api.avatars(); +const { avatars } = await api.avatars(); --- { avatars.map((avatar) => { - const ui = avatar.StuffUI("UIComponent"); - const profileAvatar = avatar.ProfileAvatar(); - return ( <>

- {ui.display_name} {Grade[ui.grade]}{" "} - {ProfileAvatarComponent_Category[profileAvatar.category]} + {avatar.stuff_ui!.display_name} {Grade[avatar.stuff_ui!.grade]}{" "} + {ProfileAvatarComponent_Category[avatar.profile_avatar!.category]}

- {ui.description &&

{ui.description}

} - {ui.obtaining_methods &&

{ui.obtaining_methods}

} - {ui.features &&

{ui.features}

} + {avatar.stuff_ui!.description && ( +

{avatar.stuff_ui!.description}

+ )} + {avatar.stuff_ui!.obtaining_methods && ( +

{avatar.stuff_ui!.obtaining_methods}

+ )} + {avatar.stuff_ui!.features &&

{avatar.stuff_ui!.features}

} - {avatar.components.sellableComponent && ( -
{JSON.stringify(avatar.Sellable(), null, 2)}
+ {avatar.sellable && ( +
{JSON.stringify(avatar.sellable, null, 2)}
)}
diff --git a/packages/website-ue/src/protos/avatar.proto b/packages/website-ue/src/protos/avatar.proto new file mode 100644 index 000000000..8d6817c7f --- /dev/null +++ b/packages/website-ue/src/protos/avatar.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package blitzkit; + +import "blitz_static_profile_avatar_component.proto"; +import "blitz_static_sellable_component.proto"; +import "blitz_static_stuff_ui_component.proto"; + +message Avatar { + string name = 1; + blitz_static_stuff_ui_component.StuffUIComponent stuff_ui = 2; + blitz_static_profile_avatar_component.ProfileAvatarComponent profile_avatar = 3; + optional blitz_static_sellable_component.SellableComponent sellable = 4; +} \ No newline at end of file diff --git a/packages/website-ue/src/protos/avatars.proto b/packages/website-ue/src/protos/avatars.proto new file mode 100644 index 000000000..57119dc5b --- /dev/null +++ b/packages/website-ue/src/protos/avatars.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package blitzkit; + +import "avatar.proto"; + +message Avatars { + repeated Avatar avatars = 1; +} \ No newline at end of file