mirror of
https://git.sr.ht/~ashkeel/strimertul
synced 2024-09-18 01:50:50 +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-2": "Go to <1>https://dev.twitch.tv/console/apps/create</1>",
|
||||
"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!",
|
||||
"app-client-id": "App Client ID",
|
||||
"app-client-secret": "App Client Secret",
|
||||
|
@ -94,7 +93,9 @@
|
|||
"auth-button": "Authenticate with Twitch",
|
||||
"auth-message": "Click the following button to authenticate {{APPNAME}} with your Twitch account:",
|
||||
"current-status": "Current status"
|
||||
}
|
||||
},
|
||||
"app-category": "Category",
|
||||
"app-oauth-redirect-url": "OAuth Redirect URLs"
|
||||
},
|
||||
"botcommands": {
|
||||
"title": "Bot commands",
|
||||
|
@ -171,7 +172,7 @@
|
|||
"enable": "Enable loyalty system",
|
||||
"currency-placeholder": "points",
|
||||
"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-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.",
|
||||
|
@ -223,12 +224,12 @@
|
|||
"reward-desc": "Description",
|
||||
"reward-cost": "Cost",
|
||||
"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-details-placeholder": "What extra details to ask the viewer for",
|
||||
"reward-details": "Require viewer details",
|
||||
"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-goals": "There are no goals configured",
|
||||
"create-goal": "Create goal",
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
TimerIcon,
|
||||
} from '@radix-ui/react-icons';
|
||||
import { EventsOff, EventsOn } from '@wailsapp/runtime/runtime';
|
||||
import { t } from 'i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
|
@ -172,6 +172,7 @@ export default function App(): JSX.Element {
|
|||
const dispatch = useAppDispatch();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [t, i18n] = useTranslation();
|
||||
|
||||
const connectToKV = async () => {
|
||||
const address = await GetKilovoltBind();
|
||||
|
@ -217,12 +218,15 @@ export default function App(): JSX.Element {
|
|||
}
|
||||
}, [ready, connected]);
|
||||
|
||||
const onboardingDone = uiConfig?.onboardingDone;
|
||||
// Sync UI changes on key change
|
||||
useEffect(() => {
|
||||
if (!onboardingDone) {
|
||||
if (uiConfig?.language) {
|
||||
void i18n.changeLanguage(uiConfig?.language ?? 'en');
|
||||
}
|
||||
if (!uiConfig?.onboardingDone) {
|
||||
navigate('/setup');
|
||||
}
|
||||
}, [ready, onboardingDone]);
|
||||
}, [ready, uiConfig]);
|
||||
|
||||
if (connected === ConnectionStatus.NotConnected) {
|
||||
return <Loading message={t('special.loading')} />;
|
||||
|
|
|
@ -86,7 +86,9 @@ function PageList({
|
|||
</ToolbarComboBox>
|
||||
</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 ? (
|
||||
<ToolbarButton
|
||||
className="button pagination-link"
|
||||
|
|
|
@ -1,31 +1,76 @@
|
|||
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 {
|
||||
label: string;
|
||||
selected?: string;
|
||||
values: {
|
||||
id: string;
|
||||
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 (
|
||||
<Root
|
||||
defaultValue={props.default}
|
||||
value={props.selected}
|
||||
aria-label={props.label}
|
||||
>
|
||||
<RadioRoot {...props}>
|
||||
{props.values.map(({ id, label }) => (
|
||||
<div key={id} style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Item value="default" id={`r${id}`}>
|
||||
<Indicator />
|
||||
</Item>
|
||||
<RadioItem value={id} id={`r${id}`}>
|
||||
<RadioIndicator />
|
||||
</RadioItem>
|
||||
<label htmlFor={`r${id}`}>{label}</label>
|
||||
</div>
|
||||
))}
|
||||
</Root>
|
||||
</RadioRoot>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -230,12 +230,12 @@ function TwitchAPISettings() {
|
|||
|
||||
<DefinitionTable
|
||||
entries={{
|
||||
'OAuth Redirect URLs': `http://${
|
||||
[t('pages.twitch-settings.app-oauth-redirect-url')]: `http://${
|
||||
httpConfig?.bind.indexOf(':') > 0
|
||||
? httpConfig.bind
|
||||
: `localhost${httpConfig?.bind ?? ':4337'}`
|
||||
}/twitch/callback`,
|
||||
Category: 'Broadcasting Suite',
|
||||
[t('pages.twitch-settings.app-category')]: 'Broadcasting Suite',
|
||||
}}
|
||||
/>
|
||||
</Step>
|
||||
|
|
|
@ -12,8 +12,13 @@ import {
|
|||
PageContainer,
|
||||
PageHeader,
|
||||
PageTitle,
|
||||
styled,
|
||||
} from '../theme';
|
||||
|
||||
const PartialWarning = styled('small', {
|
||||
color: '$yellow11',
|
||||
});
|
||||
|
||||
export default function UISettingsPage(): React.ReactElement {
|
||||
const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
|
||||
const [t, i18n] = useTranslation();
|
||||
|
@ -32,20 +37,23 @@ export default function UISettingsPage(): React.ReactElement {
|
|||
<Field size="fullWidth">
|
||||
<Label htmlFor="bind">{t('pages.uiconfig.language')}</Label>
|
||||
<RadioGroup
|
||||
label={t('pages.uiconfig.language')}
|
||||
default={i18n.resolvedLanguage}
|
||||
selected={uiConfig?.language ?? i18n.resolvedLanguage}
|
||||
aria-label={t('pages.uiconfig.language')}
|
||||
defaultValue={i18n.resolvedLanguage}
|
||||
value={uiConfig?.language ?? i18n.resolvedLanguage}
|
||||
onValueChange={(value) => {
|
||||
void dispatch(setUiConfig({ ...uiConfig, language: value }));
|
||||
}}
|
||||
values={languages.map((lang) => ({
|
||||
id: lang.code,
|
||||
label: (
|
||||
<span>
|
||||
{lang.name}{' '}
|
||||
{lang.keys < maxKeys ? (
|
||||
<small>
|
||||
<PartialWarning>
|
||||
{t('pages.uiconfig.partial-translation')} (
|
||||
{((lang.keys / maxKeys) * 100).toFixed(1)}% - {lang.keys}/
|
||||
{maxKeys})
|
||||
</small>
|
||||
</PartialWarning>
|
||||
) : null}
|
||||
</span>
|
||||
),
|
||||
|
|
|
@ -25,6 +25,9 @@ export const globalStyles = globalCss({
|
|||
color: '$teal11',
|
||||
},
|
||||
},
|
||||
p: {
|
||||
lineHeight: 1.5,
|
||||
},
|
||||
});
|
||||
|
||||
export const { styled, theme } = createStitches({
|
||||
|
|
Loading…
Reference in a new issue