i18n
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Box, ColorSwatch, Group, Text } from '@mantine/core';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { updateUserAccentColor } from '@/features/settings/server';
|
||||
import { useAuth } from '@/contexts/auth-context';
|
||||
|
||||
@@ -43,7 +44,7 @@ const AccentColorPicker = () => {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text fw={500} size='sm' mb='xs'>Accent Color</Text>
|
||||
<Text fw={500} size='sm' mb='xs'><Trans>Accent Color</Trans></Text>
|
||||
<Group gap='xs' w='100%' justify='space-between'>
|
||||
{colors.map((color) => (
|
||||
<ColorButton
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Center, Box, Text, SegmentedControl, MantineColorScheme } from '@mantine/core';
|
||||
import { SunIcon, MoonIcon, Icon, MonitorIcon } from '@phosphor-icons/react'
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { updateUserColorScheme } from '@/features/settings/server';
|
||||
import { useAuth } from '@/contexts/auth-context';
|
||||
|
||||
@@ -16,7 +17,8 @@ const ColorSchemeLabel: React.FC<ColorSchemeLabelProps> = ({ colorScheme, Icon }
|
||||
|
||||
export function ColorSchemePicker() {
|
||||
const { metadata, user, set } = useAuth()
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const handleClick = async (value: string) => {
|
||||
if (user) {
|
||||
await updateUserColorScheme({ data: value });
|
||||
@@ -26,7 +28,7 @@ export function ColorSchemePicker() {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text fw={500} size='sm' mb='xs'>Color Scheme</Text>
|
||||
<Text fw={500} size='sm' mb='xs'><Trans>Color Scheme</Trans></Text>
|
||||
<SegmentedControl
|
||||
w='100%'
|
||||
value={metadata.colorScheme}
|
||||
@@ -34,15 +36,15 @@ export function ColorSchemePicker() {
|
||||
data={[
|
||||
{
|
||||
value: 'dark',
|
||||
label: <ColorSchemeLabel colorScheme='Dark' Icon={MoonIcon} />
|
||||
label: <ColorSchemeLabel colorScheme={t`Dark`} Icon={MoonIcon} />
|
||||
},
|
||||
{
|
||||
value: 'light',
|
||||
label: <ColorSchemeLabel colorScheme='Light' Icon={SunIcon} />
|
||||
label: <ColorSchemeLabel colorScheme={t`Light`} Icon={SunIcon} />
|
||||
},
|
||||
{
|
||||
value: 'auto',
|
||||
label: <ColorSchemeLabel colorScheme='System' Icon={MonitorIcon} />
|
||||
label: <ColorSchemeLabel colorScheme={t`System`} Icon={MonitorIcon} />
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Select, Text, Box } from "@mantine/core";
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import { updateUserLocale } from "@/features/settings/server";
|
||||
import { useAuth } from "@/contexts/auth-context";
|
||||
import {
|
||||
ensureMessages,
|
||||
resolveLocale,
|
||||
LOCALE_LABELS,
|
||||
SUPPORTED_LOCALES,
|
||||
type AppLocale,
|
||||
} from "@/lib/i18n";
|
||||
|
||||
const LocalePicker = () => {
|
||||
const { metadata, user, set } = useAuth();
|
||||
const { t } = useLingui();
|
||||
|
||||
const handleChange = async (value: string | null) => {
|
||||
if (!value || !user) return;
|
||||
const locale = resolveLocale(value);
|
||||
await ensureMessages(locale);
|
||||
await updateUserLocale({ data: locale });
|
||||
set({ metadata: { ...metadata, locale } });
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Text fw={500} size="sm" mb="xs">
|
||||
<Trans>Language</Trans>
|
||||
</Text>
|
||||
<Select
|
||||
aria-label={t`Language`}
|
||||
data={SUPPORTED_LOCALES.map((locale: AppLocale) => ({
|
||||
value: locale,
|
||||
label: LOCALE_LABELS[locale],
|
||||
}))}
|
||||
value={resolveLocale(metadata.locale)}
|
||||
onChange={handleChange}
|
||||
allowDeselect={false}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocalePicker;
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Box, Title, Stack, Switch, Button, Text, Group } from "@mantine/core";
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import { useWebPush } from "@/hooks/use-web-push";
|
||||
import toast from "@/lib/sonner";
|
||||
|
||||
export function NotificationsSection() {
|
||||
const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } =
|
||||
useWebPush();
|
||||
const { t } = useLingui();
|
||||
|
||||
if (!supported || !configured) {
|
||||
return (
|
||||
@@ -13,11 +15,11 @@ export function NotificationsSection() {
|
||||
py="sm"
|
||||
style={{ borderBottom: "1px solid var(--mantine-color-default-border)" }}
|
||||
>
|
||||
<Title order={3}>Notifications</Title>
|
||||
<Title order={3}><Trans>Notifications</Trans></Title>
|
||||
<Text size="sm" c="dimmed" mt="xs">
|
||||
{supported
|
||||
? "Notifications aren't available right now."
|
||||
: "This device doesn't support push notifications. On iOS, install the app to your Home Screen first."}
|
||||
? <Trans>Notifications aren't available right now.</Trans>
|
||||
: <Trans>This device doesn't support push notifications. On iOS, install the app to your Home Screen first.</Trans>}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
@@ -27,25 +29,25 @@ export function NotificationsSection() {
|
||||
if (checked) {
|
||||
const ok = await enable();
|
||||
if (ok) {
|
||||
toast.success("Notifications enabled on this device");
|
||||
toast.success(t`Notifications enabled on this device`);
|
||||
} else if (permission === "denied") {
|
||||
toast.error(
|
||||
"Notifications are blocked. Enable them in your browser settings."
|
||||
t`Notifications are blocked. Enable them in your browser settings.`
|
||||
);
|
||||
} else {
|
||||
toast.error("Couldn't enable notifications");
|
||||
toast.error(t`Couldn't enable notifications`);
|
||||
}
|
||||
} else {
|
||||
const ok = await disable();
|
||||
if (ok) toast.success("Notifications disabled on this device");
|
||||
else toast.error("Couldn't disable notifications");
|
||||
if (ok) toast.success(t`Notifications disabled on this device`);
|
||||
else toast.error(t`Couldn't disable notifications`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTest = async () => {
|
||||
const ok = await sendTest();
|
||||
if (ok) toast.success("Test notification sent");
|
||||
else toast.error("Couldn't send test notification");
|
||||
if (ok) toast.success(t`Test notification sent`);
|
||||
else toast.error(t`Couldn't send test notification`);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -54,13 +56,13 @@ export function NotificationsSection() {
|
||||
py="sm"
|
||||
style={{ borderBottom: "1px solid var(--mantine-color-default-border)" }}
|
||||
>
|
||||
<Title order={3}>Notifications</Title>
|
||||
<Title order={3}><Trans>Notifications</Trans></Title>
|
||||
<Stack mt="xs" gap="sm">
|
||||
<Switch
|
||||
checked={subscribed}
|
||||
onChange={(e) => handleToggle(e.currentTarget.checked)}
|
||||
disabled={busy}
|
||||
label="Enable notifications on this device"
|
||||
label={t`Enable notifications on this device`}
|
||||
/>
|
||||
{subscribed && (
|
||||
<Group>
|
||||
@@ -70,7 +72,7 @@ export function NotificationsSection() {
|
||||
onClick={handleTest}
|
||||
loading={busy}
|
||||
>
|
||||
Send test notification
|
||||
<Trans>Send test notification</Trans>
|
||||
</Button>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
@@ -25,6 +25,32 @@ export const updateUserColorScheme = createServerFn({ method: "POST" })
|
||||
};
|
||||
});
|
||||
|
||||
export const updateUserLocale = createServerFn({ method: "POST" })
|
||||
.validator((data: string) => data)
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ context, data }) => {
|
||||
const { userAuthId, metadata } = context;
|
||||
if (!userAuthId) return;
|
||||
|
||||
const { SUPPORTED_LOCALES } = await import("@/lib/i18n");
|
||||
if (!(SUPPORTED_LOCALES as readonly string[]).includes(data)) return;
|
||||
|
||||
const { updateUserMetadataFields } = await import(
|
||||
"@/utils/supertokens-core.server"
|
||||
);
|
||||
|
||||
await updateUserMetadataFields(userAuthId, {
|
||||
locale: data,
|
||||
});
|
||||
|
||||
return {
|
||||
metadata: {
|
||||
...metadata,
|
||||
locale: data,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const updateUserAccentColor = createServerFn({ method: "POST" })
|
||||
.validator((data: string) => data)
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
|
||||
Reference in New Issue
Block a user