import classNames from "classnames"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { MetaResponse, getBackendMeta } from "@/backend/accounts/meta"; import { Button } from "@/components/buttons/Button"; import { Icon, Icons } from "@/components/Icon"; import { Loading } from "@/components/layout/Loading"; import { TextInputControl } from "@/components/text-inputs/TextInputControl"; interface BackendOption { url: string; meta: MetaResponse | null; loading: boolean; error: boolean; } interface BackendSelectorProps { selectedUrl: string | null; onSelect: (url: string | null) => void; availableUrls: string[]; showCustom?: boolean; } function BackendOptionItem({ option, isSelected, onClick, }: { option: BackendOption; isSelected: boolean; onClick: () => void; }) { const { t } = useTranslation(); const hostname = option.url ? new URL(option.url).hostname : undefined; return ( ); } export function BackendSelector({ selectedUrl, onSelect, availableUrls, showCustom = true, }: BackendSelectorProps) { const { t } = useTranslation(); // Helper to strip protocol from URL for display const stripProtocol = (url: string | null): string => { if (!url) return ""; return url.replace(/^https?:\/\//, ""); }; // Initialize customUrl from selectedUrl if it's a custom URL (not in availableUrls) const isCustomUrl = selectedUrl && !availableUrls.includes(selectedUrl); const [customUrl, setCustomUrl] = useState( isCustomUrl ? stripProtocol(selectedUrl) : "", ); const [backendOptions, setBackendOptions] = useState([]); // Update customUrl when selectedUrl changes and it's a custom URL useEffect(() => { if (selectedUrl && !availableUrls.includes(selectedUrl)) { setCustomUrl(stripProtocol(selectedUrl)); } }, [selectedUrl, availableUrls]); // Initialize and fetch meta for backend options useEffect(() => { const fetchMetas = async () => { const options: BackendOption[] = availableUrls.map((url) => ({ url, meta: null, loading: true, error: false, })); setBackendOptions(options); // Fetch each backend's meta independently and update state as each completes // This prevents one slow/down backend from blocking the others options.forEach(async (option) => { try { const meta = await getBackendMeta(option.url); setBackendOptions((prev) => prev.map((opt) => opt.url === option.url ? { ...opt, meta, loading: false, error: false } : opt, ), ); } catch { setBackendOptions((prev) => prev.map((opt) => opt.url === option.url ? { ...opt, meta: null, loading: false, error: true } : opt, ), ); } }); }; if (availableUrls.length > 0) { fetchMetas(); } }, [availableUrls]); const handleCustomUrlSelect = () => { if (customUrl.trim()) { let url = customUrl.trim(); if (!url.startsWith("http://") && !url.startsWith("https://")) { url = `https://${url}`; } onSelect(url); } }; const isCustomUrlSelected = selectedUrl !== null && !availableUrls.includes(selectedUrl); return (
{backendOptions.length > 0 ? (
{backendOptions.map((option) => ( onSelect(option.url)} /> ))}
) : null} {showCustom && (
{isCustomUrlSelected ? ( ) : null}

{t("auth.backendSelection.customBackend")}

{isCustomUrlSelected ? ( {t("auth.backendSelection.active")} ) : null}
)}
); }