diff options
| author | Mohamed Bassem <me@mbassem.com> | 2025-07-06 18:26:41 +0000 |
|---|---|---|
| committer | Mohamed Bassem <me@mbassem.com> | 2025-07-06 18:51:40 +0000 |
| commit | 0e94ad36b580534877871ece247ed6085b5749ef (patch) | |
| tree | 4585a326d2a2e36d34c6627c047ccec49b3e1b4d /apps/web/components/settings/UserOptions.tsx | |
| parent | b60ece578304df21602d39c7022a7a4dbc6437e0 (diff) | |
| download | karakeep-0e94ad36b580534877871ece247ed6085b5749ef.tar.zst | |
feat: Add a new timezone user setting
Diffstat (limited to 'apps/web/components/settings/UserOptions.tsx')
| -rw-r--r-- | apps/web/components/settings/UserOptions.tsx | 80 |
1 files changed, 77 insertions, 3 deletions
diff --git a/apps/web/components/settings/UserOptions.tsx b/apps/web/components/settings/UserOptions.tsx index 4f18a2e3..02c27145 100644 --- a/apps/web/components/settings/UserOptions.tsx +++ b/apps/web/components/settings/UserOptions.tsx @@ -1,13 +1,13 @@ "use client"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useClientConfig } from "@/lib/clientConfig"; import { useTranslation } from "@/lib/i18n/client"; import { useInterfaceLang } from "@/lib/userLocalSettings/bookmarksLayout"; import { updateInterfaceLang } from "@/lib/userLocalSettings/userLocalSettings"; import { useUserSettings } from "@/lib/userSettings"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Archive, Bookmark, Globe } from "lucide-react"; +import { Archive, Bookmark, Clock, Globe } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -71,6 +71,9 @@ export default function UserOptions() { }); }, }); + const [timezones, setTimezones] = useState< + { label: string; value: string }[] | null + >(null); const bookmarkClickActionTranslation: Record< ZUserSettings["bookmarkClickAction"], @@ -92,6 +95,39 @@ export default function UserOptions() { hide: t("settings.info.user_settings.archive_display_behaviour.hide"), }; + // Get all supported timezones and format them nicely + useEffect(() => { + try { + const browserTimezones = Intl.supportedValuesOf("timeZone"); + setTimezones( + browserTimezones + .map((tz) => { + // Create a more readable label by replacing underscores with spaces + // and showing the current time offset + const now = new Date(); + const formatter = new Intl.DateTimeFormat("en", { + timeZone: tz, + timeZoneName: "short", + }); + const parts = formatter.formatToParts(now); + const timeZoneName = + parts.find((part) => part.type === "timeZoneName")?.value || ""; + + // Format the timezone name for display + const displayName = tz.replace(/_/g, " ").replace("/", " / "); + const label = timeZoneName + ? `${displayName} (${timeZoneName})` + : displayName; + + return { value: tz, label }; + }) + .sort((a, b) => a.label.localeCompare(b.label)), + ); + } catch { + setTimezones(null); + } + }, []); + const form = useForm<z.infer<typeof zUserSettingsSchema>>({ resolver: zodResolver(zUserSettingsSchema), defaultValues: data, @@ -119,9 +155,47 @@ export default function UserOptions() { <LanguageSelect /> </div> + <FormField + control={form.control} + name="timezone" + render={({ field }) => ( + <div className="space-y-2"> + <Label className="flex items-center gap-2 text-sm font-medium"> + <Clock className="h-4 w-4" /> + Timezone + </Label> + <Select + disabled={!!clientConfig.demoMode || timezones === null} + value={field.value} + onValueChange={(value) => { + mutate({ + timezone: value, + }); + }} + > + <SelectTrigger className="h-11"> + <SelectValue> + {timezones?.find( + (tz: { value: string; label: string }) => + tz.value === field.value, + )?.label || field.value} + </SelectValue> + </SelectTrigger> + <SelectContent> + {timezones?.map((tz: { value: string; label: string }) => ( + <SelectItem key={tz.value} value={tz.value}> + {tz.label} + </SelectItem> + ))} + </SelectContent> + </Select> + </div> + )} + /> + <Separator /> - <div className="grid gap-6 md:grid-cols-2"> + <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> <FormField control={form.control} name="bookmarkClickAction" |
