mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: host functionality
This commit is contained in:
1 parent
3c7a10a949
commit
8512557e76
6 files changed
+93
-34
No files matched your search
@@ -9,9 +9,9 @@ interface PreviewWrapperProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PreviewWrapper({ name }: PreviewWrapperProps) {
|
export function PreviewWrapper({ name }: PreviewWrapperProps) {
|
||||||
const { useState } = useEmbedStateCurry();
|
const { useEmbedState } = useEmbedStateCurry();
|
||||||
const width = useState('width');
|
const width = useEmbedState('width');
|
||||||
const height = useState('height');
|
const height = useEmbedState('height');
|
||||||
const Preview = embedPreviews[name];
|
const Preview = embedPreviews[name];
|
||||||
const wrapper = useRef<HTMLDivElement>(null);
|
const wrapper = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import { compositeStatsKeys, fetchTankDefinitions } from '@blitzkit/core';
|
import {
|
||||||
|
compositeStatsKeys,
|
||||||
|
deltaTankStats,
|
||||||
|
fetchTankDefinitions,
|
||||||
|
getTankStats,
|
||||||
|
idToRegion,
|
||||||
|
type IndividualTankStats,
|
||||||
|
} from '@blitzkit/core';
|
||||||
import strings from '@blitzkit/core/lang/en-US.json';
|
import strings from '@blitzkit/core/lang/en-US.json';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { breakdownConfig } from '../../constants/embeds';
|
import { breakdownConfig } from '../../constants/embeds';
|
||||||
|
import { EmbedBreakdownPersistent } from '../../stores/embedBreakdownPersistent';
|
||||||
import { useEmbedStateCurry } from '../../stores/embedState/utilities';
|
import { useEmbedStateCurry } from '../../stores/embedState/utilities';
|
||||||
import { BreakdownEmbedCard, BreakdownEmbedWrapper } from '../TanksEmbed';
|
import { BreakdownEmbedCard, BreakdownEmbedWrapper } from '../TanksEmbed';
|
||||||
|
|
||||||
@@ -9,18 +18,17 @@ export const compositeStatsKeysOptions = compositeStatsKeys.map((value) => ({
|
|||||||
value,
|
value,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const tanks = await fetchTankDefinitions().then((tanks) =>
|
const tankDefinitions = await fetchTankDefinitions();
|
||||||
Object.values(tanks.tanks),
|
const tanks = Object.values(tankDefinitions);
|
||||||
);
|
|
||||||
|
|
||||||
export function BreakdownPreview() {
|
export function BreakdownPreview() {
|
||||||
const { useState } = useEmbedStateCurry<typeof breakdownConfig>();
|
const { useEmbedState } = useEmbedStateCurry<typeof breakdownConfig>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BreakdownEmbedWrapper>
|
<BreakdownEmbedWrapper>
|
||||||
{useState('showTotal') && <BreakdownEmbedCard tank={null} />}
|
{useEmbedState('showTotal') && <BreakdownEmbedCard tank={null} />}
|
||||||
|
|
||||||
{tanks.slice(0, useState('listMaxTanks')).map((tank) => (
|
{tanks.slice(0, useEmbedState('listMaxTanks')).map((tank) => (
|
||||||
<BreakdownEmbedCard key={tank.id} tank={tank} />
|
<BreakdownEmbedCard key={tank.id} tank={tank} />
|
||||||
))}
|
))}
|
||||||
</BreakdownEmbedWrapper>
|
</BreakdownEmbedWrapper>
|
||||||
@@ -28,15 +36,48 @@ export function BreakdownPreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function BreakdownRenderer() {
|
export function BreakdownRenderer() {
|
||||||
const { useState } = useEmbedStateCurry<typeof breakdownConfig>();
|
return (
|
||||||
|
<EmbedBreakdownPersistent.Provider>
|
||||||
|
<BreakdownRendererContent />
|
||||||
|
</EmbedBreakdownPersistent.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreakdownRendererContent() {
|
||||||
|
const { useEmbedState } = useEmbedStateCurry<typeof breakdownConfig>();
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const id = Number(params.get('id'));
|
||||||
|
const tankStatsA = EmbedBreakdownPersistent.use((state) => state[id] ?? []);
|
||||||
|
const [tankStatsB, setTankStatsB] = useState<IndividualTankStats[]>([]);
|
||||||
|
const diff = useMemo(
|
||||||
|
() =>
|
||||||
|
deltaTankStats(tankStatsA, tankStatsB).sort(
|
||||||
|
(a, b) => b.last_battle_time - a.last_battle_time,
|
||||||
|
),
|
||||||
|
[tankStatsB],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
const newB = await getTankStats(idToRegion(id), id);
|
||||||
|
|
||||||
|
if (newB === null) return;
|
||||||
|
|
||||||
|
setTankStatsB(newB);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BreakdownEmbedWrapper>
|
<BreakdownEmbedWrapper>
|
||||||
{useState('showTotal') && <BreakdownEmbedCard tank={null} />}
|
{useEmbedState('showTotal') && <BreakdownEmbedCard tank={null} />}
|
||||||
|
|
||||||
{tanks.slice(0, useState('listMaxTanks')).map((tank) => (
|
{diff.slice(0, useEmbedState('listMaxTanks')).map((diff) => {
|
||||||
<BreakdownEmbedCard key={tank.id} tank={tank} />
|
const tank = tankDefinitions.tanks[diff.tank_id];
|
||||||
))}
|
|
||||||
|
return <BreakdownEmbedCard key={tank.id} tank={tank} />;
|
||||||
|
})}
|
||||||
</BreakdownEmbedWrapper>
|
</BreakdownEmbedWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -25,10 +25,10 @@ interface BreakdownEmbedWrapperProps {
|
|||||||
export function BreakdownEmbedWrapper({
|
export function BreakdownEmbedWrapper({
|
||||||
children,
|
children,
|
||||||
}: BreakdownEmbedWrapperProps) {
|
}: BreakdownEmbedWrapperProps) {
|
||||||
const { useState } = useEmbedStateCurry<typeof breakdownConfig>();
|
const { useEmbedState } = useEmbedStateCurry<typeof breakdownConfig>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap={useState('listGap')} height="100%">
|
<Flex direction="column" gap={useEmbedState('listGap')} height="100%">
|
||||||
{children}
|
{children}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
@@ -39,7 +39,7 @@ interface BreakdownEmbedCardProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function BreakdownEmbedCard({ tank }: BreakdownEmbedCardProps) {
|
export function BreakdownEmbedCard({ tank }: BreakdownEmbedCardProps) {
|
||||||
const { useState, useRichText } =
|
const { useEmbedState, useRichText } =
|
||||||
useEmbedStateCurry<typeof breakdownConfig>();
|
useEmbedStateCurry<typeof breakdownConfig>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -47,17 +47,17 @@ export function BreakdownEmbedCard({ tank }: BreakdownEmbedCardProps) {
|
|||||||
flexShrink="0"
|
flexShrink="0"
|
||||||
direction="column"
|
direction="column"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: toRadiusVar(useState('cardRadius')),
|
borderRadius: toRadiusVar(useEmbedState('cardRadius')),
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
backgroundColor: useState('cardBodyBackgroundColor'),
|
backgroundColor: useEmbedState('cardBodyBackgroundColor'),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
justify="center"
|
justify="center"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: useState('cardHeaderBackgroundColor'),
|
backgroundColor: useEmbedState('cardHeaderBackgroundColor'),
|
||||||
}}
|
}}
|
||||||
p={useState('cardHeaderPadding')}
|
p={useEmbedState('cardHeaderPadding')}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
{...{
|
{...{
|
||||||
@@ -66,7 +66,7 @@ export function BreakdownEmbedCard({ tank }: BreakdownEmbedCardProps) {
|
|||||||
tank === null
|
tank === null
|
||||||
? undefined
|
? undefined
|
||||||
: tank.type === TankType.RESEARCHABLE ||
|
: tank.type === TankType.RESEARCHABLE ||
|
||||||
!useState('cardTitleTypeColor')
|
!useEmbedState('cardTitleTypeColor')
|
||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
color:
|
color:
|
||||||
@@ -77,7 +77,7 @@ export function BreakdownEmbedCard({ tank }: BreakdownEmbedCardProps) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex align="center" gap="1">
|
<Flex align="center" gap="1">
|
||||||
{useState('cardTitleClassIcon') && tank !== null && (
|
{useEmbedState('cardTitleClassIcon') && tank !== null && (
|
||||||
<>
|
<>
|
||||||
{tank.class === TankClass.LIGHT && (
|
{tank.class === TankClass.LIGHT && (
|
||||||
<ClassLight width="1em" height="1em" />
|
<ClassLight width="1em" height="1em" />
|
||||||
@@ -98,18 +98,20 @@ export function BreakdownEmbedCard({ tank }: BreakdownEmbedCardProps) {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex p={useState('cardBodyPadding')}>
|
<Flex p={useEmbedState('cardBodyPadding')}>
|
||||||
{times(4, (index) => {
|
{times(4, (index) => {
|
||||||
const column = (index + 1) as 1 | 2 | 3 | 4;
|
const column = (index + 1) as 1 | 2 | 3 | 4;
|
||||||
const columnState = useState(`column${column}`) as CompositeStatsKey;
|
const columnState = useEmbedState(
|
||||||
const customLabel = useState(`column${column}CustomLabel`);
|
`column${column}`,
|
||||||
|
) as CompositeStatsKey;
|
||||||
|
const customLabel = useEmbedState(`column${column}CustomLabel`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
key={column}
|
key={column}
|
||||||
direction="column"
|
direction="column"
|
||||||
align="center"
|
align="center"
|
||||||
gap={useState('columnGap')}
|
gap={useEmbedState('columnGap')}
|
||||||
flexGrow="1"
|
flexGrow="1"
|
||||||
>
|
>
|
||||||
<Text {...useRichText('columnValue')}>
|
<Text {...useRichText('columnValue')}>
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ export function Page({ embed }: PageProps) {
|
|||||||
|
|
||||||
function Content({ embed }: PageProps) {
|
function Content({ embed }: PageProps) {
|
||||||
const Renderer = embedRenderers[embed];
|
const Renderer = embedRenderers[embed];
|
||||||
const { useState } = useEmbedStateCurry();
|
const { useEmbedState } = useEmbedStateCurry();
|
||||||
const width = useState('width');
|
const width = useEmbedState('width');
|
||||||
const height = useState('height');
|
const height = useEmbedState('height');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BlitzKitTheme style={{ background: 'transparent' }}>
|
<BlitzKitTheme style={{ background: 'transparent' }}>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { type IndividualTankStats } from '@blitzkit/core';
|
||||||
|
import * as lodash from 'lodash-es';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { createContextualStore } from '../core/zustand/createContextualStore';
|
||||||
|
|
||||||
|
type EmbedBreakdownPersistentStore = Record<number, IndividualTankStats[]>;
|
||||||
|
|
||||||
|
export const EmbedBreakdownPersistent = createContextualStore(() =>
|
||||||
|
create<EmbedBreakdownPersistentStore>()(
|
||||||
|
persist(() => ({}), {
|
||||||
|
name: 'embed-breakdown',
|
||||||
|
merge: (a, b) => lodash.merge(b, a),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
@@ -3,7 +3,7 @@ import { EmbedState, type EmbedConfig, type ExtractEmbedConfigTypes } from '.';
|
|||||||
import type { EmbedConfigItemType, EmbedItemType } from './constants';
|
import type { EmbedConfigItemType, EmbedItemType } from './constants';
|
||||||
|
|
||||||
export function useEmbedStateCurry<Config extends EmbedConfig>() {
|
export function useEmbedStateCurry<Config extends EmbedConfig>() {
|
||||||
function useState<Key extends keyof Config>(key: Key) {
|
function useEmbedState<Key extends keyof Config>(key: Key) {
|
||||||
return EmbedState.use((state) => {
|
return EmbedState.use((state) => {
|
||||||
return (state as ExtractEmbedConfigTypes<Config>)[key];
|
return (state as ExtractEmbedConfigTypes<Config>)[key];
|
||||||
});
|
});
|
||||||
@@ -16,7 +16,7 @@ export function useEmbedStateCurry<Config extends EmbedConfig>() {
|
|||||||
: never;
|
: never;
|
||||||
}[keyof Config],
|
}[keyof Config],
|
||||||
>(key: Key) {
|
>(key: Key) {
|
||||||
const { color, ...state } = useState(
|
const { color, ...state } = useEmbedState(
|
||||||
key,
|
key,
|
||||||
) as EmbedConfigItemType<EmbedItemType.RichText>['default'];
|
) as EmbedConfigItemType<EmbedItemType.RichText>['default'];
|
||||||
|
|
||||||
@@ -26,5 +26,5 @@ export function useEmbedStateCurry<Config extends EmbedConfig>() {
|
|||||||
} satisfies TextProps;
|
} satisfies TextProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { useState, useRichText };
|
return { useEmbedState, useRichText };
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user