mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-07 01:40:38 +01:00
Compare commits
3
Commits
ea412086d4
...
f5f3f70e35
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5f3f70e35 | ||
|
|
2d282a3261 | ||
|
|
a2d0d2d8c0 |
No files matched your search
@@ -2,13 +2,18 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 7 / 8;
|
||||
border-radius: var(--space-3);
|
||||
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
outline: var(--outline-interactive);
|
||||
box-shadow: var(--shadow-subtle);
|
||||
}
|
||||
|
||||
.avatar-label {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { api } from "../../core/api/dynamic";
|
||||
import { useAwait } from "../../hooks/useAwait";
|
||||
import type { Avatar } from "../../protos/avatar";
|
||||
import { Text } from "../Text";
|
||||
import "./index.css";
|
||||
@@ -7,13 +9,22 @@ interface Props {
|
||||
}
|
||||
|
||||
export function AvatarCard({ avatar }: Props) {
|
||||
// const { locale } = useLocale();
|
||||
const { locale } = { locale: "en" };
|
||||
const profileAvatarEntityStrings = useAwait(
|
||||
() => api.groupedGameStrings(locale, "ProfileAvatarEntity"),
|
||||
"profile-avatar-entity-strings"
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="avatar-card">
|
||||
<div
|
||||
className="avatar-image"
|
||||
style={{ backgroundImage: `url(/api/avatars/${avatar.name}.webp)` }}
|
||||
/>
|
||||
<Text className="avatar-label">{avatar.name}</Text>
|
||||
<Text align="center" className="avatar-label">
|
||||
{profileAvatarEntityStrings[avatar.stuff_ui!.display_name]}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -47,6 +47,8 @@ import "@fontsource-variable/roboto-flex";
|
||||
var(--space-1) var(--white-a3),
|
||||
inset calc(var(--space-1) * sin(45deg) * -1)
|
||||
calc(var(--space-1) * sin(45deg) * -1) var(--space-1) var(--black-a3);
|
||||
--shadow-subtle: var(--space-1) var(--space-1) var(--space-4)
|
||||
var(--black-a11);
|
||||
|
||||
--blur-glass: blur(4rem);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { BlitzKitIcon } from "../icons/BlitzKit";
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
margin-bottom: calc(var(--space-5) * -1);
|
||||
margin-bottom: calc(var(--space-4) * -1);
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
&[data-size="minor"] {
|
||||
font-size: var(--font-size-minor);
|
||||
}
|
||||
|
||||
&[data-size="major"] {
|
||||
font-size: var(--font-size-major);
|
||||
}
|
||||
@@ -12,20 +11,23 @@
|
||||
&[data-weight="light"] {
|
||||
font-weight: var(--weight-light);
|
||||
}
|
||||
|
||||
&[data-weight="regular"] {
|
||||
font-weight: var(--weight-regular);
|
||||
}
|
||||
|
||||
&[data-weight="medium"] {
|
||||
font-weight: var(--weight-medium);
|
||||
}
|
||||
|
||||
&[data-weight="bold"] {
|
||||
font-weight: var(--weight-bold);
|
||||
}
|
||||
|
||||
&[data-weight="black"] {
|
||||
font-weight: var(--weight-black);
|
||||
}
|
||||
|
||||
&[data-align="center"] {
|
||||
text-align: center;
|
||||
}
|
||||
&[data-align="right"] {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export interface TextProps extends ComponentProps<"span"> {
|
||||
weight?: "light" | "regular" | "medium" | "bold" | "black";
|
||||
lowContrast?: boolean;
|
||||
size?: "regular" | "minor" | "major";
|
||||
align?: "left" | "center" | "right";
|
||||
}
|
||||
|
||||
export function Text({
|
||||
@@ -17,6 +18,7 @@ export function Text({
|
||||
size = "regular",
|
||||
weight,
|
||||
style,
|
||||
align,
|
||||
...props
|
||||
}: TextProps) {
|
||||
return (
|
||||
@@ -25,6 +27,7 @@ export function Text({
|
||||
style={{ color: `var(--${color}-${lowContrast ? 11 : 12})`, ...style }}
|
||||
data-size={size}
|
||||
data-weight={weight}
|
||||
data-align={align === "left" ? undefined : align}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -63,29 +63,42 @@ export abstract class AbstractAPI {
|
||||
return this._stringsCache[locale];
|
||||
}
|
||||
|
||||
private _gameStringsCache: Record<
|
||||
string,
|
||||
Record<string, Record<string, string>>
|
||||
> = {};
|
||||
private _gameStringsCache: Record<string, Record<string, string>> = {};
|
||||
protected abstract _gameStrings(
|
||||
locale: string,
|
||||
prefix: string
|
||||
locale: string
|
||||
): Promise<Record<string, string>>;
|
||||
async gameStrings(locale: string, prefix: string) {
|
||||
async gameStrings(locale: string) {
|
||||
this.assertLocale(locale);
|
||||
|
||||
if (this._gameStringsCache[locale] === undefined) {
|
||||
this._gameStringsCache[locale] = {};
|
||||
this._gameStringsCache[locale] = await this._gameStrings(locale);
|
||||
}
|
||||
|
||||
if (this._gameStringsCache[locale][prefix] === undefined) {
|
||||
this._gameStringsCache[locale][prefix] = await this._gameStrings(
|
||||
locale,
|
||||
prefix
|
||||
);
|
||||
return this._gameStringsCache[locale];
|
||||
}
|
||||
|
||||
private _groupedGameStringsCache: Record<
|
||||
string,
|
||||
Record<string, Record<string, string>>
|
||||
> = {};
|
||||
protected abstract _groupedGameStrings(
|
||||
locale: string,
|
||||
group: string,
|
||||
prefix: boolean
|
||||
): Promise<Record<string, string>>;
|
||||
async groupedGameStrings(locale: string, group: string, prefix = true) {
|
||||
this.assertLocale(locale);
|
||||
|
||||
if (this._groupedGameStringsCache[locale] === undefined) {
|
||||
this._groupedGameStringsCache[locale] = {};
|
||||
}
|
||||
|
||||
return this._gameStringsCache[locale][prefix];
|
||||
if (this._groupedGameStringsCache[locale][group] === undefined) {
|
||||
this._groupedGameStringsCache[locale][group] =
|
||||
await this._groupedGameStrings(locale, group, prefix);
|
||||
}
|
||||
|
||||
return this._groupedGameStringsCache[locale][group];
|
||||
}
|
||||
|
||||
private _avatarCache: Record<string, Avatar> = {};
|
||||
@@ -127,4 +140,14 @@ export abstract class AbstractAPI {
|
||||
|
||||
return this._backgroundsCache;
|
||||
}
|
||||
|
||||
private _gameStringGroupsCache: string[] | undefined;
|
||||
protected abstract _gameStringGroups(): Promise<string[]>;
|
||||
async gameStringGroups() {
|
||||
if (this._gameStringGroupsCache === undefined) {
|
||||
this._gameStringGroupsCache = await this._gameStringGroups();
|
||||
}
|
||||
|
||||
return this._gameStringGroupsCache;
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,27 @@ export class ClientAPI extends AbstractAPI {
|
||||
protected _avatars() {
|
||||
return fetchPB("/api/avatars/all.pb", Avatars);
|
||||
}
|
||||
|
||||
protected _gameStrings(): Promise<Record<string, string>> {
|
||||
throw new Error("Cannot fetch full game strings on client");
|
||||
}
|
||||
|
||||
protected async _groupedGameStrings(
|
||||
locale: string,
|
||||
group: string,
|
||||
prefix: boolean
|
||||
) {
|
||||
const response = await fetch(`/api/game-strings/${locale}/${group}.json`);
|
||||
const json = (await response.json()) as Record<string, string>;
|
||||
|
||||
if (!prefix) return json;
|
||||
|
||||
const strings: Record<string, string> = {};
|
||||
|
||||
for (const key in json) {
|
||||
strings[`${group}__${key}`] = json[key];
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export class ServerAPI extends AbstractAPI {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _gameStrings(locale: string, prefix: string) {
|
||||
protected async _gameStrings(locale: string) {
|
||||
const group = this.metadata.group("ClientConfigsEntity");
|
||||
|
||||
if (group.length !== 1) {
|
||||
@@ -98,11 +98,20 @@ export class ServerAPI extends AbstractAPI {
|
||||
);
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
protected async _groupedGameStrings(
|
||||
locale: string,
|
||||
group: string,
|
||||
prefix: boolean
|
||||
) {
|
||||
const strings = await this.gameStrings(locale);
|
||||
const filtered: Record<string, string> = {};
|
||||
|
||||
for (const key in strings) {
|
||||
if (key.startsWith(`${prefix}__`)) {
|
||||
filtered[key] = strings[key];
|
||||
if (key.startsWith(`${group}__`)) {
|
||||
filtered[prefix ? key : key.substring(group.length + 2)] = strings[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +133,7 @@ export class ServerAPI extends AbstractAPI {
|
||||
protected async _tankList() {
|
||||
const group = this.metadata.group("TankEntity");
|
||||
const list: TankListEntry[] = [];
|
||||
const strings = await this.gameStrings("en", "TankEntity");
|
||||
const strings = await this.groupedGameStrings("en", "TankEntity");
|
||||
|
||||
for (const item of group) {
|
||||
const tankCatalog = item.TankCatalog();
|
||||
@@ -214,4 +223,16 @@ export class ServerAPI extends AbstractAPI {
|
||||
|
||||
return { name, stuff_ui, profile_background, sellable };
|
||||
}
|
||||
|
||||
protected async _gameStringGroups() {
|
||||
const defaultGameStrings = await this.gameStrings(locales.default);
|
||||
const groups = new Set<string>();
|
||||
|
||||
for (const key in defaultGameStrings) {
|
||||
const [group] = key.split("__");
|
||||
groups.add(group);
|
||||
}
|
||||
|
||||
return Array.from(groups.values());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
import Section from "../../../components/Section.astro";
|
||||
import BaseLayout from "../../../layouts/BaseLayout.astro";
|
||||
import { Page } from "./_index";
|
||||
|
||||
@@ -11,12 +12,14 @@ const { locale } = Astro.props;
|
||||
.avatars {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--space-7), 1fr));
|
||||
gap: var(--space-4);
|
||||
gap: var(--space-5) var(--space-4);
|
||||
}
|
||||
</style>
|
||||
|
||||
<BaseLayout {locale} accent="gold">
|
||||
<Page client:only="react">
|
||||
<Page skeleton slot="fallback" />
|
||||
</Page>
|
||||
<Section>
|
||||
<Page client:only="react">
|
||||
<Page skeleton slot="fallback" />
|
||||
</Page>
|
||||
</Section>
|
||||
</BaseLayout>
|
||||
@@ -21,7 +21,7 @@ export const getStaticPaths = (() => {
|
||||
const { locale } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout accent="amber" noNavbar>
|
||||
<BaseLayout {locale} accent="amber" noNavbar>
|
||||
<ColorScale color="purple" slot="head" />
|
||||
<ColorScale color="gold" slot="head" />
|
||||
<ColorScale color="lime" slot="head" />
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import locales from "@blitzkit/i18n/locales.json";
|
||||
import type { GetStaticPaths, GetStaticPathsResult } from "astro";
|
||||
import { api } from "../../../../core/api/dynamic";
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const paths: GetStaticPathsResult = [];
|
||||
const groups = await api.gameStringGroups();
|
||||
|
||||
for (const { locale } of locales.supported) {
|
||||
for (const group of groups) {
|
||||
paths.push({ params: { locale, group } });
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
export async function GET({
|
||||
params,
|
||||
}: {
|
||||
params: { locale: string; group: string };
|
||||
}) {
|
||||
const strings = await api.groupedGameStrings(
|
||||
params.locale,
|
||||
params.group,
|
||||
false
|
||||
);
|
||||
return Response.json(strings);
|
||||
}
|
||||
Reference in new issue
Block a user