mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: translate embeds
This commit is contained in:
1 parent
baa16840df
commit
9aee8feaf4
13 files changed
+227
-81
No files matched your search
@@ -10,6 +10,7 @@ const SUPPORTED_LOCALES_DISCORD_MAP: Record<SupportedLocale, Locale> = {
|
|||||||
zh: Locale.ChineseCN,
|
zh: Locale.ChineseCN,
|
||||||
fr: Locale.French,
|
fr: Locale.French,
|
||||||
ja: Locale.Japanese,
|
ja: Locale.Japanese,
|
||||||
|
pl: Locale.Polish,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SUPPORTED_LOCALES_DISCORD_MAP_INVERSE = Object.fromEntries(
|
export const SUPPORTED_LOCALES_DISCORD_MAP_INVERSE = Object.fromEntries(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const SUPPORTED_LOCALES = [
|
|||||||
'ru',
|
'ru',
|
||||||
'uk',
|
'uk',
|
||||||
'zh',
|
'zh',
|
||||||
|
'pl',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
||||||
@@ -22,6 +23,7 @@ export const SUPPORTED_LOCALE_BLITZ_MAP: Record<SupportedLocale, string> = {
|
|||||||
ru: 'ru',
|
ru: 'ru',
|
||||||
uk: 'uk',
|
uk: 'uk',
|
||||||
zh: 'zh-Hans',
|
zh: 'zh-Hans',
|
||||||
|
pl: 'pl',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
|
export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
|
||||||
@@ -33,4 +35,5 @@ export const SUPPORTED_LOCALE_FLAGS: Record<SupportedLocale, string> = {
|
|||||||
ru: '🇷🇺',
|
ru: '🇷🇺',
|
||||||
uk: '🇺🇦',
|
uk: '🇺🇦',
|
||||||
zh: '🇨🇳',
|
zh: '🇨🇳',
|
||||||
|
pl: '🇵🇱',
|
||||||
};
|
};
|
||||||
@@ -52,7 +52,8 @@
|
|||||||
"uk": "Ukrainian",
|
"uk": "Ukrainian",
|
||||||
"zh": "Chinese",
|
"zh": "Chinese",
|
||||||
"ja": "Japanese",
|
"ja": "Japanese",
|
||||||
"fr": "French"
|
"fr": "French",
|
||||||
|
"pl": "Polish"
|
||||||
},
|
},
|
||||||
|
|
||||||
"wn8_percentile": {
|
"wn8_percentile": {
|
||||||
@@ -741,6 +742,12 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"common": {
|
"common": {
|
||||||
|
"player_search": {
|
||||||
|
"hint": "Search players...",
|
||||||
|
"no_results": "No results",
|
||||||
|
"searching": "Searching..."
|
||||||
|
},
|
||||||
|
|
||||||
"copy_button": {
|
"copy_button": {
|
||||||
"copied": "Copied!"
|
"copied": "Copied!"
|
||||||
},
|
},
|
||||||
@@ -1211,7 +1218,84 @@
|
|||||||
"name": "Embed",
|
"name": "Embed",
|
||||||
"button": "Create now",
|
"button": "Create now",
|
||||||
"description": "Embeds for streaming",
|
"description": "Embeds for streaming",
|
||||||
"seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization"
|
"seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization",
|
||||||
|
"coming_soon": "More embeds coming soon.",
|
||||||
|
"suggest": "Suggest ideas.",
|
||||||
|
|
||||||
|
"preview": {
|
||||||
|
"title": "Example preview",
|
||||||
|
"upload": "Upload test background"
|
||||||
|
},
|
||||||
|
|
||||||
|
"configuration": {
|
||||||
|
"import": {
|
||||||
|
"title": "Import",
|
||||||
|
"description": "Paste your current URL if you have one already to import your settings",
|
||||||
|
"hint": "Paste URL...",
|
||||||
|
"apply": "Apply",
|
||||||
|
"idiot_title": "Invalid URL",
|
||||||
|
"idiot_description": "Try pasting the URL that BlitzKit previously generated, you silly goose!",
|
||||||
|
"idiot_button": "Okay, my bad"
|
||||||
|
},
|
||||||
|
|
||||||
|
"export": {
|
||||||
|
"title": "Export",
|
||||||
|
"description": "Copy all properties for you streaming software",
|
||||||
|
"generate": {
|
||||||
|
"button": "Generate URL",
|
||||||
|
"title": "Generate URL",
|
||||||
|
"subtitle": "Search an account to track",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"success": {
|
||||||
|
"title": "Copy URL",
|
||||||
|
"info": "Generated the URL for %s1. The embed will start tracking as soon as the URL is pasted. Reset at any time by right-clicking on PC or long-pressing on mobile and then selecting \"Reset\".",
|
||||||
|
"back": "Back",
|
||||||
|
"copy": "Copy URL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"css": "Copy CSS",
|
||||||
|
"width": "Copy Width",
|
||||||
|
"height": "Copy Height",
|
||||||
|
"framerate": "Copy Framerate"
|
||||||
|
},
|
||||||
|
|
||||||
|
"customize": {
|
||||||
|
"title": "Customize",
|
||||||
|
"reset": "Reset",
|
||||||
|
"description": "Bells and whistles of your embed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"types": {
|
||||||
|
"breakdown": {
|
||||||
|
"name": "Breakdown",
|
||||||
|
"options": {
|
||||||
|
"width": "Width",
|
||||||
|
"height": "Height",
|
||||||
|
"showTotal": "Show total",
|
||||||
|
"listGap": "List gap",
|
||||||
|
"listMaxTanks": "Max tanks",
|
||||||
|
"cardRadius": "Card radius",
|
||||||
|
"cardHeaderBackgroundColor": "Card header background color",
|
||||||
|
"cardHeaderPadding": "Card header padding",
|
||||||
|
"cardTitle": "Card title",
|
||||||
|
"cardTitleTypeColor": "Card title type color",
|
||||||
|
"cardTitleClassIcon": "Card title class icon",
|
||||||
|
"cardBodyBackgroundColor": "Card body background color",
|
||||||
|
"cardBodyPadding": "Card body padding",
|
||||||
|
"columnGap": "Column gap",
|
||||||
|
"columnValue": "Column value",
|
||||||
|
"columnLabel": "Column label",
|
||||||
|
"column1": "Column 1",
|
||||||
|
"column2": "Column 2",
|
||||||
|
"column3": "Column 3",
|
||||||
|
"column4": "Column 4",
|
||||||
|
"column1CustomLabel": "Column 1 label",
|
||||||
|
"column2CustomLabel": "Column 2 label",
|
||||||
|
"column3CustomLabel": "Column 3 label"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"charts": {
|
"charts": {
|
||||||
"name": "Charts",
|
"name": "Charts",
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Select } from '@radix-ui/themes';
|
import { Select } from '@radix-ui/themes';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index';
|
import type { EmbedPreviewControllerProps } from '../../pages/[...locale]/tools/embed/[embed]/_index';
|
||||||
import { EmbedState } from '../../stores/embedState';
|
import { EmbedState } from '../../stores/embedState';
|
||||||
import type {
|
import type {
|
||||||
@@ -13,6 +14,7 @@ export function Enum({
|
|||||||
config: EmbedConfigItemType<EmbedItemType.Enum>;
|
config: EmbedConfigItemType<EmbedItemType.Enum>;
|
||||||
}) {
|
}) {
|
||||||
const mutateEmbedState = EmbedState.useMutation();
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
const { locale } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select.Root
|
<Select.Root
|
||||||
@@ -27,7 +29,7 @@ export function Enum({
|
|||||||
<Select.Content>
|
<Select.Content>
|
||||||
{config.options.map((option) => (
|
{config.options.map((option) => (
|
||||||
<Select.Item key={option.value} value={option.value}>
|
<Select.Item key={option.value} value={option.value}>
|
||||||
{option.label}
|
{option.locales[locale]}
|
||||||
</Select.Item>
|
</Select.Item>
|
||||||
))}
|
))}
|
||||||
</Select.Content>
|
</Select.Content>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
type AccountListItem,
|
type AccountListItem,
|
||||||
type AccountListWithServer,
|
type AccountListWithServer,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import strings from '@blitzkit/i18n/strings/en.json';
|
|
||||||
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
|
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
@@ -19,6 +18,8 @@ import { debounce } from 'lodash-es';
|
|||||||
import { useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { stringify } from 'urlon';
|
import { stringify } from 'urlon';
|
||||||
import type { embedConfigurations } from '../../constants/embeds';
|
import type { embedConfigurations } from '../../constants/embeds';
|
||||||
|
import { literals } from '../../core/i18n/literals';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { EmbedState, type EmbedStateStore } from '../../stores/embedState';
|
import { EmbedState, type EmbedStateStore } from '../../stores/embedState';
|
||||||
import { CopyButton } from '../CopyButton';
|
import { CopyButton } from '../CopyButton';
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
const input = useRef<HTMLInputElement>(null);
|
const input = useRef<HTMLInputElement>(null);
|
||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
const [results, setResults] = useState<AccountListWithServer>([]);
|
const [results, setResults] = useState<AccountListWithServer>([]);
|
||||||
|
const { strings } = useLocale();
|
||||||
const performSearch = useCallback(
|
const performSearch = useCallback(
|
||||||
debounce(async () => {
|
debounce(async () => {
|
||||||
if (!input.current) return;
|
if (!input.current) return;
|
||||||
@@ -72,20 +74,27 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
<Dialog.Trigger>
|
<Dialog.Trigger>
|
||||||
<Button>
|
<Button>
|
||||||
<Link2Icon />
|
<Link2Icon />
|
||||||
Generate URL
|
{strings.website.tools.embed.configuration.export.generate.button}
|
||||||
</Button>
|
</Button>
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
|
|
||||||
<Dialog.Content width="fit-content">
|
<Dialog.Content width="fit-content">
|
||||||
<Flex direction="column" gap="1">
|
<Flex direction="column" gap="1">
|
||||||
<Heading size="5">Generate URL</Heading>
|
<Heading size="5">
|
||||||
<Text color="gray">Search an account to track</Text>
|
{strings.website.tools.embed.configuration.export.generate.title}
|
||||||
|
</Heading>
|
||||||
|
<Text color="gray">
|
||||||
|
{
|
||||||
|
strings.website.tools.embed.configuration.export.generate
|
||||||
|
.subtitle
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<TextField.Root
|
<TextField.Root
|
||||||
mt="4"
|
mt="4"
|
||||||
style={{ width: '20rem' }}
|
style={{ width: '20rem' }}
|
||||||
placeholder="Search players..."
|
placeholder={strings.website.common.player_search.hint}
|
||||||
ref={input}
|
ref={input}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
@@ -98,12 +107,14 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
{searching && (
|
{searching && (
|
||||||
<Text color="gray">
|
<Text color="gray">
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<Spinner /> Searching
|
<Spinner /> {strings.website.common.player_search.searching}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{!searching && results.length === 0 && (
|
{!searching && results.length === 0 && (
|
||||||
<Text color="gray">No results</Text>
|
<Text color="gray">
|
||||||
|
{strings.website.common.player_search.no_results}
|
||||||
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!searching && results.length > 0 && (
|
{!searching && results.length > 0 && (
|
||||||
@@ -131,7 +142,7 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
|
|
||||||
<Dialog.Close>
|
<Dialog.Close>
|
||||||
<Button color="red" variant="outline" style={{ width: '100%' }}>
|
<Button color="red" variant="outline" style={{ width: '100%' }}>
|
||||||
Cancel
|
{strings.website.tools.embed.configuration.export.generate.cancel}
|
||||||
</Button>
|
</Button>
|
||||||
</Dialog.Close>
|
</Dialog.Close>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
@@ -140,12 +151,18 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
<Dialog.Root open={copyDialogOpen} onOpenChange={setCopyDialogOpen}>
|
<Dialog.Root open={copyDialogOpen} onOpenChange={setCopyDialogOpen}>
|
||||||
<Dialog.Content style={{ width: 'fit-content' }}>
|
<Dialog.Content style={{ width: 'fit-content' }}>
|
||||||
<Flex direction="column" gap="1">
|
<Flex direction="column" gap="1">
|
||||||
<Heading size="5">Copy URL</Heading>
|
<Heading size="5">
|
||||||
|
{
|
||||||
|
strings.website.tools.embed.configuration.export.generate
|
||||||
|
.success.title
|
||||||
|
}
|
||||||
|
</Heading>
|
||||||
<Text color="gray">
|
<Text color="gray">
|
||||||
Generated the URL for {copyUser?.nickname}. The embed will start
|
{literals(
|
||||||
tracking as soon as the URL is pasted. Reset at any time by
|
strings.website.tools.embed.configuration.export.generate
|
||||||
right-clicking on PC or long-pressing on mobile and then selecting
|
.success.info,
|
||||||
"Reset".
|
[`${copyUser?.nickname}`],
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
@@ -158,7 +175,11 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
setSearchDialogOpen(true);
|
setSearchDialogOpen(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CaretLeftIcon /> Back
|
<CaretLeftIcon />{' '}
|
||||||
|
{
|
||||||
|
strings.website.tools.embed.configuration.export.generate
|
||||||
|
.success.back
|
||||||
|
}
|
||||||
</Button>
|
</Button>
|
||||||
<CopyButton
|
<CopyButton
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
@@ -186,7 +207,11 @@ export function GenerateURL({ embed }: GenerateURLProps) {
|
|||||||
return `${location.origin}/tools/embed/${embed}/host?${searchParams.toString()}`;
|
return `${location.origin}/tools/embed/${embed}/host?${searchParams.toString()}`;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link2Icon /> Copy URL
|
<Link2Icon />{' '}
|
||||||
|
{
|
||||||
|
strings.website.tools.embed.configuration.export.generate
|
||||||
|
.success.copy
|
||||||
|
}
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { parse } from 'urlon';
|
import { parse } from 'urlon';
|
||||||
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
import { EmbedState } from '../../stores/embedState';
|
import { EmbedState } from '../../stores/embedState';
|
||||||
|
|
||||||
export function Import() {
|
export function Import() {
|
||||||
@@ -16,12 +17,15 @@ export function Import() {
|
|||||||
const [showInvalidURLPrompt, setShowInvalidURLPrompt] = useState(false);
|
const [showInvalidURLPrompt, setShowInvalidURLPrompt] = useState(false);
|
||||||
const [showConfirmation, setConfirmation] = useState(false);
|
const [showConfirmation, setConfirmation] = useState(false);
|
||||||
const mutateEmbedState = EmbedState.useMutation();
|
const mutateEmbedState = EmbedState.useMutation();
|
||||||
|
const { strings } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading>Import</Heading>
|
<Heading>
|
||||||
|
{strings.website.tools.embed.configuration.import.title}
|
||||||
|
</Heading>
|
||||||
<Text size="2" color="gray" mb="2">
|
<Text size="2" color="gray" mb="2">
|
||||||
Paste your current URL if you have one already to import your settings
|
{strings.website.tools.embed.configuration.import.description}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<AlertDialog.Root
|
<AlertDialog.Root
|
||||||
@@ -29,15 +33,18 @@ export function Import() {
|
|||||||
onOpenChange={setShowInvalidURLPrompt}
|
onOpenChange={setShowInvalidURLPrompt}
|
||||||
>
|
>
|
||||||
<AlertDialog.Content>
|
<AlertDialog.Content>
|
||||||
<AlertDialog.Title>Invalid URL</AlertDialog.Title>
|
<AlertDialog.Title>
|
||||||
|
{strings.website.tools.embed.configuration.import.idiot_title}
|
||||||
|
</AlertDialog.Title>
|
||||||
<AlertDialog.Description>
|
<AlertDialog.Description>
|
||||||
Try pasting the URL that BlitzKit previously generated, you silly
|
{strings.website.tools.embed.configuration.import.idiot_description}
|
||||||
goose!
|
|
||||||
</AlertDialog.Description>
|
</AlertDialog.Description>
|
||||||
|
|
||||||
<Flex gap="3" mt="4" justify="end">
|
<Flex gap="3" mt="4" justify="end">
|
||||||
<AlertDialog.Cancel>
|
<AlertDialog.Cancel>
|
||||||
<Button>Okay, my bad</Button>
|
<Button>
|
||||||
|
{strings.website.tools.embed.configuration.import.idiot_button}
|
||||||
|
</Button>
|
||||||
</AlertDialog.Cancel>
|
</AlertDialog.Cancel>
|
||||||
</Flex>
|
</Flex>
|
||||||
</AlertDialog.Content>
|
</AlertDialog.Content>
|
||||||
@@ -46,7 +53,7 @@ export function Import() {
|
|||||||
<Flex gap="2" mb="6">
|
<Flex gap="2" mb="6">
|
||||||
<TextField.Root
|
<TextField.Root
|
||||||
ref={importInput}
|
ref={importInput}
|
||||||
placeholder="Paste URL..."
|
placeholder={strings.website.tools.embed.configuration.import.hint}
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
/>
|
/>
|
||||||
<Tooltip content="Applied!" open={showConfirmation}>
|
<Tooltip content="Applied!" open={showConfirmation}>
|
||||||
@@ -72,7 +79,7 @@ export function Import() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Apply
|
{strings.website.tools.embed.configuration.import.apply}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
sumCompositeStats,
|
sumCompositeStats,
|
||||||
type IndividualTankStats,
|
type IndividualTankStats,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import strings from '@blitzkit/i18n/strings/en.json';
|
import { localizedStrings } from '@blitzkit/i18n';
|
||||||
import { ReloadIcon } from '@radix-ui/react-icons';
|
import { ReloadIcon } from '@radix-ui/react-icons';
|
||||||
import { ContextMenu } from '@radix-ui/themes';
|
import { ContextMenu } from '@radix-ui/themes';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
@@ -20,8 +20,16 @@ import { useEmbedStateCurry } from '../../stores/embedState/utilities';
|
|||||||
import { BreakdownEmbedCard, BreakdownEmbedWrapper } from '../TanksEmbed';
|
import { BreakdownEmbedCard, BreakdownEmbedWrapper } from '../TanksEmbed';
|
||||||
|
|
||||||
export const compositeStatsKeysOptions = compositeStatsKeys.map((value) => ({
|
export const compositeStatsKeysOptions = compositeStatsKeys.map((value) => ({
|
||||||
label: strings.common.composite_stats[value],
|
|
||||||
value,
|
value,
|
||||||
|
locales: Object.entries(localizedStrings).reduce(
|
||||||
|
(accumulator, [locale, strings]) => {
|
||||||
|
return {
|
||||||
|
...accumulator,
|
||||||
|
[locale]: strings.common.composite_stats[value],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const [tankDefinitions, averageDefinitions] = await Promise.all([
|
const [tankDefinitions, averageDefinitions] = await Promise.all([
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
WidthIcon,
|
WidthIcon,
|
||||||
} from '@radix-ui/react-icons';
|
} from '@radix-ui/react-icons';
|
||||||
import { Box, Button, Flex, Heading, ScrollArea, Text } from '@radix-ui/themes';
|
import { Box, Button, Flex, Heading, ScrollArea, Text } from '@radix-ui/themes';
|
||||||
import { capitalize, startCase } from 'lodash-es';
|
|
||||||
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
||||||
import { CopyButton } from '../../../../../components/CopyButton';
|
import { CopyButton } from '../../../../../components/CopyButton';
|
||||||
import { Boolean } from '../../../../../components/Embeds/Boolean';
|
import { Boolean } from '../../../../../components/Embeds/Boolean';
|
||||||
@@ -29,6 +28,11 @@ import {
|
|||||||
extractEmbedConfigDefaults,
|
extractEmbedConfigDefaults,
|
||||||
} from '../../../../../constants/embeds';
|
} from '../../../../../constants/embeds';
|
||||||
import { NAVBAR_HEIGHT } from '../../../../../constants/navbar';
|
import { NAVBAR_HEIGHT } from '../../../../../constants/navbar';
|
||||||
|
import {
|
||||||
|
LocaleProvider,
|
||||||
|
useLocale,
|
||||||
|
type LocaleAcceptorProps,
|
||||||
|
} from '../../../../../hooks/useLocale';
|
||||||
import { App } from '../../../../../stores/app';
|
import { App } from '../../../../../stores/app';
|
||||||
import { EmbedState, type EmbedConfig } from '../../../../../stores/embedState';
|
import { EmbedState, type EmbedConfig } from '../../../../../stores/embedState';
|
||||||
import { EmbedItemType } from '../../../../../stores/embedState/constants';
|
import { EmbedItemType } from '../../../../../stores/embedState/constants';
|
||||||
@@ -41,15 +45,17 @@ interface PageProps {
|
|||||||
embed: keyof typeof embedConfigurations;
|
embed: keyof typeof embedConfigurations;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Page({ embed }: PageProps) {
|
export function Page({ embed, locale }: PageProps & LocaleAcceptorProps) {
|
||||||
const config = embedConfigurations[embed];
|
const config = embedConfigurations[embed];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<App.Provider>
|
<LocaleProvider locale={locale}>
|
||||||
<EmbedState.Provider data={extractEmbedConfigDefaults(config)}>
|
<App.Provider>
|
||||||
<Content embed={embed} />
|
<EmbedState.Provider data={extractEmbedConfigDefaults(config)}>
|
||||||
</EmbedState.Provider>
|
<Content embed={embed} />
|
||||||
</App.Provider>
|
</EmbedState.Provider>
|
||||||
|
</App.Provider>
|
||||||
|
</LocaleProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +66,7 @@ function Content({ embed }: PageProps) {
|
|||||||
imgur('SO13zur', { format: 'jpeg' }),
|
imgur('SO13zur', { format: 'jpeg' }),
|
||||||
);
|
);
|
||||||
const fileInput = useRef<HTMLInputElement>();
|
const fileInput = useRef<HTMLInputElement>();
|
||||||
|
const { strings } = useLocale();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fileInput.current = document.createElement('input');
|
fileInput.current = document.createElement('input');
|
||||||
@@ -83,9 +90,11 @@ function Content({ embed }: PageProps) {
|
|||||||
<Flex direction="column" gap="2" p="4">
|
<Flex direction="column" gap="2" p="4">
|
||||||
<Import />
|
<Import />
|
||||||
|
|
||||||
<Heading>Export</Heading>
|
<Heading>
|
||||||
|
{strings.website.tools.embed.configuration.export.title}
|
||||||
|
</Heading>
|
||||||
<Text size="2" color="gray" mb="2">
|
<Text size="2" color="gray" mb="2">
|
||||||
Copy all properties for you streaming software
|
{strings.website.tools.embed.configuration.export.description}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Flex mb="6" gap="2" wrap="wrap">
|
<Flex mb="6" gap="2" wrap="wrap">
|
||||||
@@ -98,30 +107,32 @@ function Content({ embed }: PageProps) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CodeIcon />
|
<CodeIcon />
|
||||||
Copy CSS
|
{strings.website.tools.embed.configuration.export.css}
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
<CopyButton
|
<CopyButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
copy={() => `${embedStateStore.getState().width}`}
|
copy={() => `${embedStateStore.getState().width}`}
|
||||||
>
|
>
|
||||||
<WidthIcon />
|
<WidthIcon />
|
||||||
Copy width
|
{strings.website.tools.embed.configuration.export.width}
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
<CopyButton
|
<CopyButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
copy={() => `${embedStateStore.getState().height}`}
|
copy={() => `${embedStateStore.getState().height}`}
|
||||||
>
|
>
|
||||||
<HeightIcon />
|
<HeightIcon />
|
||||||
Copy height
|
{strings.website.tools.embed.configuration.export.height}
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
<CopyButton variant="outline" copy={() => '1'}>
|
<CopyButton variant="outline" copy={() => '1'}>
|
||||||
<TimerIcon />
|
<TimerIcon />
|
||||||
Copy framerate
|
{strings.website.tools.embed.configuration.export.framerate}
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex justify="between" align="center">
|
<Flex justify="between" align="center">
|
||||||
<Heading>Customize</Heading>
|
<Heading>
|
||||||
|
{strings.website.tools.embed.configuration.customize.title}
|
||||||
|
</Heading>
|
||||||
<Button
|
<Button
|
||||||
color="red"
|
color="red"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -132,11 +143,12 @@ function Content({ embed }: PageProps) {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ResetIcon /> Reset
|
<ResetIcon />{' '}
|
||||||
|
{strings.website.tools.embed.configuration.customize.reset}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text size="2" color="gray" mb="2">
|
<Text size="2" color="gray" mb="2">
|
||||||
Bells and whistles of your embed
|
{strings.website.tools.embed.configuration.customize.description}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{Object.keys(config).map((configKey) => {
|
{Object.keys(config).map((configKey) => {
|
||||||
@@ -206,7 +218,14 @@ function Content({ embed }: PageProps) {
|
|||||||
mb={oneLiner ? '2' : '4'}
|
mb={oneLiner ? '2' : '4'}
|
||||||
pb={item.pad ? '6' : undefined}
|
pb={item.pad ? '6' : undefined}
|
||||||
>
|
>
|
||||||
<Text>{capitalize(startCase(configKey as string))}</Text>
|
<Text>
|
||||||
|
{
|
||||||
|
(
|
||||||
|
strings.website.tools.embed.types[embed]
|
||||||
|
.options as Record<string, string>
|
||||||
|
)[configKey]
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
<Flex
|
<Flex
|
||||||
gap="2"
|
gap="2"
|
||||||
align="center"
|
align="center"
|
||||||
@@ -246,14 +265,16 @@ function Content({ embed }: PageProps) {
|
|||||||
backgroundColor: 'var(--color-panel-solid)',
|
backgroundColor: 'var(--color-panel-solid)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text color="gray">Example preview</Text>
|
<Text color="gray">
|
||||||
|
{strings.website.tools.embed.preview.title}
|
||||||
|
</Text>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="gray"
|
color="gray"
|
||||||
onClick={() => fileInput.current?.click()}
|
onClick={() => fileInput.current?.click()}
|
||||||
>
|
>
|
||||||
<ImageIcon /> Upload test background
|
<ImageIcon /> {strings.website.tools.embed.preview.upload}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export const getStaticPaths = (() => {
|
|||||||
)
|
)
|
||||||
.flat();
|
.flat();
|
||||||
}) satisfies GetStaticPaths;
|
}) satisfies GetStaticPaths;
|
||||||
|
|
||||||
|
const { locale } = Astro.params;
|
||||||
---
|
---
|
||||||
|
|
||||||
<title>BlitzKit - {capitalize(Astro.params.embed)} embed</title>
|
<title>BlitzKit - {capitalize(Astro.params.embed)} embed</title>
|
||||||
@@ -44,5 +46,5 @@ export const getStaticPaths = (() => {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<Page client:load embed={Astro.params.embed} />
|
<Page {locale} client:load embed={Astro.params.embed} />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { imgur, ImgurSize } from '@blitzkit/core';
|
import { imgur, ImgurSize } from '@blitzkit/core';
|
||||||
import { Box, Flex, Link, Text } from '@radix-ui/themes';
|
import { Box, Flex, Link, Text } from '@radix-ui/themes';
|
||||||
import { capitalize } from 'lodash-es';
|
|
||||||
import { PreviewWrapper } from '../../../../components/Embeds/PreviewWrapper';
|
import { PreviewWrapper } from '../../../../components/Embeds/PreviewWrapper';
|
||||||
import { LinkI18n } from '../../../../components/LinkI18n';
|
import { LinkI18n } from '../../../../components/LinkI18n';
|
||||||
import { PageWrapper } from '../../../../components/PageWrapper';
|
import { PageWrapper } from '../../../../components/PageWrapper';
|
||||||
@@ -9,52 +8,37 @@ import {
|
|||||||
extractEmbedConfigDefaults,
|
extractEmbedConfigDefaults,
|
||||||
} from '../../../../constants/embeds';
|
} from '../../../../constants/embeds';
|
||||||
import { Var } from '../../../../core/radix/var';
|
import { Var } from '../../../../core/radix/var';
|
||||||
import { useLocale } from '../../../../hooks/useLocale';
|
import {
|
||||||
|
LocaleProvider,
|
||||||
|
useLocale,
|
||||||
|
type LocaleAcceptorProps,
|
||||||
|
} from '../../../../hooks/useLocale';
|
||||||
import { App } from '../../../../stores/app';
|
import { App } from '../../../../stores/app';
|
||||||
import { EmbedState } from '../../../../stores/embedState';
|
import { EmbedState } from '../../../../stores/embedState';
|
||||||
|
|
||||||
export function Page() {
|
export function Page({ locale }: LocaleAcceptorProps) {
|
||||||
return (
|
return (
|
||||||
<App.Provider>
|
<LocaleProvider locale={locale}>
|
||||||
<Content />
|
<App.Provider>
|
||||||
</App.Provider>
|
<Content />
|
||||||
|
</App.Provider>
|
||||||
|
</LocaleProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Content() {
|
function Content() {
|
||||||
// const wargaming = App.useDeferred((state) => state.logins.wargaming, {
|
const { locale, strings } = useLocale();
|
||||||
// token: '',
|
|
||||||
// id: 0,
|
|
||||||
// expires: 0,
|
|
||||||
// });
|
|
||||||
|
|
||||||
const { locale } = useLocale();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="red">
|
<PageWrapper color="red">
|
||||||
{/* {!wargaming && (
|
|
||||||
<Flex direction="column" align="center" gap="2">
|
|
||||||
<Callout.Root>
|
|
||||||
<Callout.Icon>
|
|
||||||
<ExclamationTriangleIcon />
|
|
||||||
</Callout.Icon>
|
|
||||||
<Callout.Text>
|
|
||||||
<Flex align="center">You must log in to generate embeds.</Flex>
|
|
||||||
</Callout.Text>
|
|
||||||
</Callout.Root>
|
|
||||||
|
|
||||||
<WargamingLoginButton>Log in with Wargaming</WargamingLoginButton>
|
|
||||||
</Flex>
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
<Text align="center" mt="3">
|
<Text align="center" mt="3">
|
||||||
More embeds coming soon.{' '}
|
{strings.website.tools.embed.coming_soon}{' '}
|
||||||
<Link
|
<Link
|
||||||
underline="always"
|
underline="always"
|
||||||
href="https://discord.gg/nDt7AjGJQH"
|
href="https://discord.gg/nDt7AjGJQH"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Suggest ideas
|
{strings.website.tools.embed.suggest}
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
</Text>
|
</Text>
|
||||||
@@ -86,7 +70,13 @@ function Content() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex justify="center" p="2">
|
<Flex justify="center" p="2">
|
||||||
<Text>{capitalize(embed)}</Text>
|
<Text>
|
||||||
|
{
|
||||||
|
strings.website.tools.embed.types[
|
||||||
|
embed as keyof typeof strings.website.tools.embed.types
|
||||||
|
].name
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import ToolLayout from '../../../../layouts/ToolLayout.astro';
|
|||||||
import { Page } from './_index';
|
import { Page } from './_index';
|
||||||
|
|
||||||
export { getStaticPaths } from '../../index.astro';
|
export { getStaticPaths } from '../../index.astro';
|
||||||
|
|
||||||
|
const { locale } = Astro.params;
|
||||||
---
|
---
|
||||||
|
|
||||||
<ToolLayout tool={embedTool}>
|
<ToolLayout tool={embedTool}>
|
||||||
<Page />
|
<Page {locale} />
|
||||||
</ToolLayout>
|
</ToolLayout>
|
||||||
@@ -94,8 +94,8 @@ export type EmbedConfigItem = (
|
|||||||
type: EmbedItemType.Enum;
|
type: EmbedItemType.Enum;
|
||||||
default: string;
|
default: string;
|
||||||
options: {
|
options: {
|
||||||
label: string;
|
|
||||||
value: string;
|
value: string;
|
||||||
|
locales: Record<string, string>;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
) & {
|
) & {
|
||||||
|
|||||||
Reference in new issue
Block a user