"use client"; 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, Clock, Globe } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { useUpdateUserSettings } from "@karakeep/shared-react/hooks/users"; import { langNameMappings } from "@karakeep/shared/langs"; import { ZUserSettings, zUserSettingsSchema, } from "@karakeep/shared/types/users"; import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; import { Form, FormField } from "../ui/form"; import { Label } from "../ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../ui/select"; import { toast } from "../ui/use-toast"; const LanguageSelect = () => { const lang = useInterfaceLang(); return ( { await updateInterfaceLang(val); }} > {Object.entries(langNameMappings).map(([lang, name]) => ( {name} ))} ); }; export default function UserOptions() { const { t } = useTranslation(); const clientConfig = useClientConfig(); const data = useUserSettings(); const { mutate } = useUpdateUserSettings({ onSuccess: () => { toast({ description: t("settings.info.user_settings.user_settings_updated"), }); }, onError: () => { toast({ description: t("common.something_went_wrong"), variant: "destructive", }); }, }); const [timezones, setTimezones] = useState< { label: string; value: string }[] | null >(null); const bookmarkClickActionTranslation: Record< ZUserSettings["bookmarkClickAction"], string > = { open_original_link: t( "settings.info.user_settings.bookmark_click_action.open_external_url", ), expand_bookmark_preview: t( "settings.info.user_settings.bookmark_click_action.open_bookmark_details", ), }; const archiveDisplayBehaviourTranslation: Record< ZUserSettings["archiveDisplayBehaviour"], string > = { show: t("settings.info.user_settings.archive_display_behaviour.show"), 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>({ resolver: zodResolver(zUserSettingsSchema), defaultValues: data, }); // When the actual user setting is loaded, reset the form to the current value useEffect(() => { form.reset(data); }, [data]); return ( {t("settings.info.options")} {t("settings.info.interface_lang")} ( Timezone { mutate({ timezone: value, }); }} > {timezones?.find( (tz: { value: string; label: string }) => tz.value === field.value, )?.label || field.value} {timezones?.map((tz: { value: string; label: string }) => ( {tz.label} ))} )} /> ( {t( "settings.info.user_settings.bookmark_click_action.title", )} { mutate({ bookmarkClickAction: value as ZUserSettings["bookmarkClickAction"], }); }} > {bookmarkClickActionTranslation[field.value]} {Object.entries(bookmarkClickActionTranslation).map( ([value, label]) => ( {label} ), )} )} /> ( {t( "settings.info.user_settings.archive_display_behaviour.title", )} { mutate({ archiveDisplayBehaviour: value as ZUserSettings["archiveDisplayBehaviour"], }); }} > {archiveDisplayBehaviourTranslation[field.value]} {Object.entries(archiveDisplayBehaviourTranslation).map( ([value, label]) => ( {label} ), )} )} /> ); }