mirror of
https://git.sr.ht/~ashkeel/strimertul
synced 2024-09-20 02:00:49 +00:00
feat: language selector works!
This commit is contained in:
parent
bf8cd57fff
commit
031f6692bf
7 changed files with 94 additions and 31 deletions
|
@ -58,7 +58,6 @@
|
||||||
"apiguide-1": "You will need to create an application, here's how:",
|
"apiguide-1": "You will need to create an application, here's how:",
|
||||||
"apiguide-2": "Go to <1>https://dev.twitch.tv/console/apps/create</1>",
|
"apiguide-2": "Go to <1>https://dev.twitch.tv/console/apps/create</1>",
|
||||||
"apiguide-3": "Use the following data for the required fields:",
|
"apiguide-3": "Use the following data for the required fields:",
|
||||||
"oauth-redir-uri": "OAuth Redirect URLs",
|
|
||||||
"apiguide-4": "Once made, create a <1>New Secret</1>, then copy both fields below and save!",
|
"apiguide-4": "Once made, create a <1>New Secret</1>, then copy both fields below and save!",
|
||||||
"app-client-id": "App Client ID",
|
"app-client-id": "App Client ID",
|
||||||
"app-client-secret": "App Client Secret",
|
"app-client-secret": "App Client Secret",
|
||||||
|
@ -94,7 +93,9 @@
|
||||||
"auth-button": "Authenticate with Twitch",
|
"auth-button": "Authenticate with Twitch",
|
||||||
"auth-message": "Click the following button to authenticate {{APPNAME}} with your Twitch account:",
|
"auth-message": "Click the following button to authenticate {{APPNAME}} with your Twitch account:",
|
||||||
"current-status": "Current status"
|
"current-status": "Current status"
|
||||||
}
|
},
|
||||||
|
"app-category": "Category",
|
||||||
|
"app-oauth-redirect-url": "OAuth Redirect URLs"
|
||||||
},
|
},
|
||||||
"botcommands": {
|
"botcommands": {
|
||||||
"title": "Bot commands",
|
"title": "Bot commands",
|
||||||
|
@ -171,7 +172,7 @@
|
||||||
"enable": "Enable loyalty system",
|
"enable": "Enable loyalty system",
|
||||||
"currency-placeholder": "points",
|
"currency-placeholder": "points",
|
||||||
"currency-name": "Currency name",
|
"currency-name": "Currency name",
|
||||||
"currency-name-hint": "This will be appended like this: \"user has X yourcurrency\" so choose a lowercase plural name",
|
"currency-name-hint": "This will be appended like this: \"user has X yourcurrency\" so choose a lowercase plural name (ex. points)",
|
||||||
"bonus-points": "Bonus points for active users",
|
"bonus-points": "Bonus points for active users",
|
||||||
"bonus-points-hint": "Extra amount of points awarded to people who have been chatting in the last set interval",
|
"bonus-points-hint": "Extra amount of points awarded to people who have been chatting in the last set interval",
|
||||||
"note": "Note: Unlike platform-native systems (eg. Twitch channel points), this relies on chat activity rather than actual viewing status.",
|
"note": "Note: Unlike platform-native systems (eg. Twitch channel points), this relies on chat activity rather than actual viewing status.",
|
||||||
|
@ -223,12 +224,12 @@
|
||||||
"reward-desc": "Description",
|
"reward-desc": "Description",
|
||||||
"reward-cost": "Cost",
|
"reward-cost": "Cost",
|
||||||
"reward-id-hint": "This is what viewers will have to write to claim, ie. \"!redeem reward-id-here\".",
|
"reward-id-hint": "This is what viewers will have to write to claim, ie. \"!redeem reward-id-here\".",
|
||||||
"reward-name-hint": "This is what viewers will see they claimed ie. \"USER has claimed REWARDNAME",
|
"reward-name-hint": "This is what viewers will see they claimed ie. \"USER has claimed REWARDNAME\"",
|
||||||
"reward-cooldown": "Cooldown",
|
"reward-cooldown": "Cooldown",
|
||||||
"reward-details-placeholder": "What extra details to ask the viewer for",
|
"reward-details-placeholder": "What extra details to ask the viewer for",
|
||||||
"reward-details": "Require viewer details",
|
"reward-details": "Require viewer details",
|
||||||
"edit-reward": "Edit reward",
|
"edit-reward": "Edit reward",
|
||||||
"remove-reward-title": "Remore reward \"{{name}}\"?",
|
"remove-reward-title": "Remove reward \"{{name}}\"?",
|
||||||
"no-rewards": "There are no loyalty rewards, why not start with an Hydrate or Stretch?",
|
"no-rewards": "There are no loyalty rewards, why not start with an Hydrate or Stretch?",
|
||||||
"no-goals": "There are no goals configured",
|
"no-goals": "There are no goals configured",
|
||||||
"create-goal": "Create goal",
|
"create-goal": "Create goal",
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
TimerIcon,
|
TimerIcon,
|
||||||
} from '@radix-ui/react-icons';
|
} from '@radix-ui/react-icons';
|
||||||
import { EventsOff, EventsOn } from '@wailsapp/runtime/runtime';
|
import { EventsOff, EventsOn } from '@wailsapp/runtime/runtime';
|
||||||
import { t } from 'i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -172,6 +172,7 @@ export default function App(): JSX.Element {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [t, i18n] = useTranslation();
|
||||||
|
|
||||||
const connectToKV = async () => {
|
const connectToKV = async () => {
|
||||||
const address = await GetKilovoltBind();
|
const address = await GetKilovoltBind();
|
||||||
|
@ -217,12 +218,15 @@ export default function App(): JSX.Element {
|
||||||
}
|
}
|
||||||
}, [ready, connected]);
|
}, [ready, connected]);
|
||||||
|
|
||||||
const onboardingDone = uiConfig?.onboardingDone;
|
// Sync UI changes on key change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!onboardingDone) {
|
if (uiConfig?.language) {
|
||||||
|
void i18n.changeLanguage(uiConfig?.language ?? 'en');
|
||||||
|
}
|
||||||
|
if (!uiConfig?.onboardingDone) {
|
||||||
navigate('/setup');
|
navigate('/setup');
|
||||||
}
|
}
|
||||||
}, [ready, onboardingDone]);
|
}, [ready, uiConfig]);
|
||||||
|
|
||||||
if (connected === ConnectionStatus.NotConnected) {
|
if (connected === ConnectionStatus.NotConnected) {
|
||||||
return <Loading message={t('special.loading')} />;
|
return <Loading message={t('special.loading')} />;
|
||||||
|
|
|
@ -86,7 +86,9 @@ function PageList({
|
||||||
</ToolbarComboBox>
|
</ToolbarComboBox>
|
||||||
</ToolbarSection>
|
</ToolbarSection>
|
||||||
<ToolbarSection>
|
<ToolbarSection>
|
||||||
<div style={{ padding: '0 0.25rem' }}>{t('pagination.page')}</div>
|
<div style={{ padding: '0 0.25rem' }}>
|
||||||
|
{t('pagination.page', { page: current })}
|
||||||
|
</div>
|
||||||
{current > min ? (
|
{current > min ? (
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
className="button pagination-link"
|
className="button pagination-link"
|
||||||
|
|
|
@ -1,31 +1,76 @@
|
||||||
import React, { ReactElement } from 'react';
|
import React, { ReactElement } from 'react';
|
||||||
import { Root, Item, Indicator } from '@radix-ui/react-radio-group';
|
import {
|
||||||
|
Root,
|
||||||
|
Item,
|
||||||
|
Indicator,
|
||||||
|
RadioGroupProps as RootProps,
|
||||||
|
} from '@radix-ui/react-radio-group';
|
||||||
|
import { styled } from '~/ui/theme';
|
||||||
|
|
||||||
export interface RadioGroupProps {
|
export interface RadioGroupProps {
|
||||||
label: string;
|
|
||||||
selected?: string;
|
|
||||||
values: {
|
values: {
|
||||||
id: string;
|
id: string;
|
||||||
label: string | ReactElement;
|
label: string | ReactElement;
|
||||||
}[];
|
}[];
|
||||||
default?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RadioGroup(props: RadioGroupProps) {
|
const RadioRoot = styled(Root, {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px',
|
||||||
|
margin: '0.5rem 0',
|
||||||
|
'& label': {
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RadioItem = styled(Item, {
|
||||||
|
backgroundColor: '$gray12',
|
||||||
|
borderRadius: '100%',
|
||||||
|
width: '22px',
|
||||||
|
height: '22px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
border: '0',
|
||||||
|
marginRight: '0.5rem',
|
||||||
|
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: '$teal12',
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
boxShadow: '0 0 0 2px $gray2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RadioIndicator = styled(Indicator, {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
position: 'relative',
|
||||||
|
'&::after': {
|
||||||
|
content: '',
|
||||||
|
display: 'block',
|
||||||
|
width: '10px',
|
||||||
|
height: '10px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: '$teal9',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function RadioGroup(props: RadioGroupProps & RootProps) {
|
||||||
return (
|
return (
|
||||||
<Root
|
<RadioRoot {...props}>
|
||||||
defaultValue={props.default}
|
|
||||||
value={props.selected}
|
|
||||||
aria-label={props.label}
|
|
||||||
>
|
|
||||||
{props.values.map(({ id, label }) => (
|
{props.values.map(({ id, label }) => (
|
||||||
<div key={id} style={{ display: 'flex', alignItems: 'center' }}>
|
<div key={id} style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<Item value="default" id={`r${id}`}>
|
<RadioItem value={id} id={`r${id}`}>
|
||||||
<Indicator />
|
<RadioIndicator />
|
||||||
</Item>
|
</RadioItem>
|
||||||
<label htmlFor={`r${id}`}>{label}</label>
|
<label htmlFor={`r${id}`}>{label}</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Root>
|
</RadioRoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,12 +230,12 @@ function TwitchAPISettings() {
|
||||||
|
|
||||||
<DefinitionTable
|
<DefinitionTable
|
||||||
entries={{
|
entries={{
|
||||||
'OAuth Redirect URLs': `http://${
|
[t('pages.twitch-settings.app-oauth-redirect-url')]: `http://${
|
||||||
httpConfig?.bind.indexOf(':') > 0
|
httpConfig?.bind.indexOf(':') > 0
|
||||||
? httpConfig.bind
|
? httpConfig.bind
|
||||||
: `localhost${httpConfig?.bind ?? ':4337'}`
|
: `localhost${httpConfig?.bind ?? ':4337'}`
|
||||||
}/twitch/callback`,
|
}/twitch/callback`,
|
||||||
Category: 'Broadcasting Suite',
|
[t('pages.twitch-settings.app-category')]: 'Broadcasting Suite',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Step>
|
</Step>
|
||||||
|
|
|
@ -12,8 +12,13 @@ import {
|
||||||
PageContainer,
|
PageContainer,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
PageTitle,
|
PageTitle,
|
||||||
|
styled,
|
||||||
} from '../theme';
|
} from '../theme';
|
||||||
|
|
||||||
|
const PartialWarning = styled('small', {
|
||||||
|
color: '$yellow11',
|
||||||
|
});
|
||||||
|
|
||||||
export default function UISettingsPage(): React.ReactElement {
|
export default function UISettingsPage(): React.ReactElement {
|
||||||
const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
|
const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
|
||||||
const [t, i18n] = useTranslation();
|
const [t, i18n] = useTranslation();
|
||||||
|
@ -32,20 +37,23 @@ export default function UISettingsPage(): React.ReactElement {
|
||||||
<Field size="fullWidth">
|
<Field size="fullWidth">
|
||||||
<Label htmlFor="bind">{t('pages.uiconfig.language')}</Label>
|
<Label htmlFor="bind">{t('pages.uiconfig.language')}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
label={t('pages.uiconfig.language')}
|
aria-label={t('pages.uiconfig.language')}
|
||||||
default={i18n.resolvedLanguage}
|
defaultValue={i18n.resolvedLanguage}
|
||||||
selected={uiConfig?.language ?? i18n.resolvedLanguage}
|
value={uiConfig?.language ?? i18n.resolvedLanguage}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
void dispatch(setUiConfig({ ...uiConfig, language: value }));
|
||||||
|
}}
|
||||||
values={languages.map((lang) => ({
|
values={languages.map((lang) => ({
|
||||||
id: lang.code,
|
id: lang.code,
|
||||||
label: (
|
label: (
|
||||||
<span>
|
<span>
|
||||||
{lang.name}{' '}
|
{lang.name}{' '}
|
||||||
{lang.keys < maxKeys ? (
|
{lang.keys < maxKeys ? (
|
||||||
<small>
|
<PartialWarning>
|
||||||
{t('pages.uiconfig.partial-translation')} (
|
{t('pages.uiconfig.partial-translation')} (
|
||||||
{((lang.keys / maxKeys) * 100).toFixed(1)}% - {lang.keys}/
|
{((lang.keys / maxKeys) * 100).toFixed(1)}% - {lang.keys}/
|
||||||
{maxKeys})
|
{maxKeys})
|
||||||
</small>
|
</PartialWarning>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
|
|
|
@ -25,6 +25,9 @@ export const globalStyles = globalCss({
|
||||||
color: '$teal11',
|
color: '$teal11',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
p: {
|
||||||
|
lineHeight: 1.5,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { styled, theme } = createStitches({
|
export const { styled, theme } = createStitches({
|
||||||
|
|
Loading…
Reference in a new issue