import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Check, Copy, Eye, EyeOff, KeyRound, Loader2, RefreshCw } from 'lucide-react'; import { multiVirtualPrinterApi } from '../api/client'; import { Card, CardContent } from './Card'; import { ConfirmModal } from './ConfirmModal'; import { useToast } from '../contexts/ToastContext'; import { useAuth } from '../contexts/AuthContext'; import { copyTextToClipboard } from '../utils/clipboard'; /** * The signed-in user's personal access code for virtual printers (#1430), * at the top of Settings > Virtual Printer. * * Entered in their slicer instead of a virtual printer's own access code, it * marks what they send as theirs. A virtual printer checks at every sign-in * whether they may send to it. Renders nothing while auth is off. */ export function SlicerAccessCodeCard() { const { t } = useTranslation(); const { showToast } = useToast(); const queryClient = useQueryClient(); const { user, authEnabled } = useAuth(); const [revealed, setRevealed] = useState(false); const [copied, setCopied] = useState(false); const [confirmRenew, setConfirmRenew] = useState(false); // Per user: on a shared PC the next person to sign in must never see the // code cached for the one before. const queryKey = ['my-slicer-access-code', user?.id ?? null]; const enabled = authEnabled && !!user; const { data, isLoading, isError } = useQuery({ queryKey, queryFn: multiVirtualPrinterApi.getMyAccessCode, enabled, gcTime: 0, }); const renew = useMutation({ mutationFn: multiVirtualPrinterApi.renewMyAccessCode, onSuccess: (result) => { queryClient.setQueryData(queryKey, result); setRevealed(true); showToast(t('slicerAccessCode.renewed')); }, onError: (error: Error) => showToast(error.message || t('slicerAccessCode.loadFailed'), 'error'), }); if (!enabled) return null; const copy = async () => { if (!data) return; if (await copyTextToClipboard(data.code)) { setCopied(true); setTimeout(() => setCopied(false), 2000); } else { showToast(t('slicerAccessCode.copyFailed'), 'error'); } }; const iconButton = 'inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded bg-bambu-dark-secondary border border-bambu-dark-tertiary text-white hover:border-bambu-gray disabled:opacity-50 transition-colors'; return (

{t('slicerAccessCode.title')}

{isLoading ? ( ) : isError || !data ? (

{t('slicerAccessCode.loadFailed')}

) : (
{revealed ? data.code : '•'.repeat(data.code.length)}
)}

{t('slicerAccessCode.description')}

  • {t('slicerAccessCode.hintChecked')}
  • {t('slicerAccessCode.hintCamera')}
  • {t('slicerAccessCode.hintSecret')}
{confirmRenew && ( { setConfirmRenew(false); renew.mutate(); }} onCancel={() => setConfirmRenew(false)} /> )}
); }