import { createPortal } from 'react-dom'; import { useEffect, useState } from 'react'; import { Wand2, RotateCcw, FolderOpen, Trash2, X } from 'lucide-react'; import { Account, DeviceProfile, DeviceProfileVersion } from '../../types/account'; import * as accountService from '../../services/accountService'; import { useTranslation } from 'react-i18next'; import { isTauri } from '../../utils/env'; interface DeviceFingerprintDialogProps { account: Account | null; onClose: () => void; } export default function DeviceFingerprintDialog({ account, onClose }: DeviceFingerprintDialogProps) { const { t } = useTranslation(); const [deviceProfiles, setDeviceProfiles] = useState<{ current_storage?: DeviceProfile; history?: DeviceProfileVersion[]; baseline?: DeviceProfile } | null>(null); const [loadingDevice, setLoadingDevice] = useState(false); const [actionLoading, setActionLoading] = useState(null); const [actionMessage, setActionMessage] = useState(null); const [confirmProfile, setConfirmProfile] = useState(null); const [confirmType, setConfirmType] = useState<'generate' | 'restoreOriginal' | null>(null); const fetchDevice = async (target?: Account | null) => { if (!target) { setDeviceProfiles(null); return; } setLoadingDevice(true); try { const res = await accountService.getDeviceProfiles(target.id); setDeviceProfiles(res); } catch (e: any) { const errorMsg = typeof e === 'string' ? e : e.message || ''; const translated = errorMsg === 'storage_json_not_found' ? t('accounts.device_fingerprint_dialog.storage_json_not_found') : (typeof e === 'string' ? e : t('accounts.device_fingerprint_dialog.failed_to_load_device_info')); setActionMessage(translated); } finally { setLoadingDevice(false); } }; useEffect(() => { fetchDevice(account); }, [account]); const handleGeneratePreview = async () => { setActionLoading('preview'); try { const profile = await accountService.previewGenerateProfile(); setConfirmProfile(profile); setConfirmType('generate'); } catch (e: any) { setActionMessage(typeof e === 'string' ? e : t('accounts.device_fingerprint_dialog.generation_failed')); } finally { setActionLoading(null); } }; const handleConfirmGenerate = async () => { if (!account || !confirmProfile) return; setActionLoading('generate'); try { await accountService.bindDeviceProfileWithProfile(account.id, confirmProfile); setActionMessage(t('accounts.device_fingerprint_dialog.generated_and_bound')); setConfirmProfile(null); setConfirmType(null); await fetchDevice(account); // Refresh history } catch (e: any) { setActionMessage(typeof e === 'string' ? e : t('accounts.device_fingerprint_dialog.binding_failed')); } finally { setActionLoading(null); } }; const handleRestoreOriginalConfirm = () => { if (!deviceProfiles?.baseline) { setActionMessage(t('accounts.device_fingerprint_dialog.original_fingerprint_not_found')); return; } setConfirmProfile(deviceProfiles.baseline); setConfirmType('restoreOriginal'); }; const handleRestoreOriginal = async () => { if (!account) return; setActionLoading('restore'); try { const msg = await accountService.restoreOriginalDevice(); setActionMessage(msg || t('accounts.device_fingerprint_dialog.restored')); setConfirmProfile(null); setConfirmType(null); await fetchDevice(account); } catch (e: any) { setActionMessage(typeof e === 'string' ? e : t('accounts.device_fingerprint_dialog.restoration_failed')); } finally { setActionLoading(null); } }; const handleRestoreVersion = async (versionId: string) => { if (!account) return; setActionLoading(`restore-${versionId}`); try { await accountService.restoreDeviceVersion(account.id, versionId); setActionMessage(t('accounts.device_fingerprint_dialog.restored')); await fetchDevice(account); } catch (e: any) { setActionMessage(typeof e === 'string' ? e : t('accounts.device_fingerprint_dialog.restoration_failed')); } finally { setActionLoading(null); } }; const handleDeleteVersion = async (versionId: string, isCurrent?: boolean) => { if (!account || isCurrent) return; setActionLoading(`delete-${versionId}`); try { await accountService.deleteDeviceVersion(account.id, versionId); setActionMessage(t('accounts.device_fingerprint_dialog.deleted')); await fetchDevice(account); } catch (e: any) { setActionMessage(typeof e === 'string' ? e : t('accounts.device_fingerprint_dialog.deletion_failed')); } finally { setActionLoading(null); } }; const handleOpenFolder = async () => { setActionLoading('open-folder'); try { await accountService.openDeviceFolder(); setActionMessage(t('accounts.device_fingerprint_dialog.directory_opened')); } catch (e: any) { setActionMessage(typeof e === 'string' ? e : t('accounts.device_fingerprint_dialog.directory_open_failed')); } finally { setActionLoading(null); } }; const renderProfile = (profile?: DeviceProfile) => { if (!profile) return {t('common.empty') || 'çİş'}; return (
machineId: {profile.machine_id}
macMachineId: {profile.mac_machine_id}
devDeviceId: {profile.dev_device_id}
sqmId: {profile.sqm_id}
); }; if (!account) return null; return createPortal(

{t('accounts.device_fingerprint_dialog.title')}

{account.email}
{t('accounts.device_fingerprint_dialog.operations')}
{isTauri() && ( )}
{actionMessage &&
{actionMessage}
}
{t('accounts.device_fingerprint_dialog.current_storage')}
{t('accounts.device_fingerprint_dialog.effective')}

{t('accounts.device_fingerprint_dialog.current_storage_desc')}

{loadingDevice ?
{t('accounts.device_fingerprint_dialog.loading')}
: renderProfile(deviceProfiles?.current_storage)}
{t('accounts.device_fingerprint_dialog.account_binding')}
{t('accounts.device_fingerprint_dialog.pending_application')}

{t('accounts.device_fingerprint_dialog.account_binding_desc')}

{/* Bound fingerprint = the one with is_current in current history */} {loadingDevice ? (
{t('accounts.device_fingerprint_dialog.loading')}
) : ( renderProfile(deviceProfiles?.history?.find(h => h.is_current)?.profile) )}
{t('accounts.device_fingerprint_dialog.historical_fingerprints')}
{loadingDevice ? (
{t('accounts.device_fingerprint_dialog.loading')}
) : (
{deviceProfiles?.history && deviceProfiles.history.map(v => ( handleRestoreVersion(v.id)} onDelete={() => handleDeleteVersion(v.id, v.is_current)} loadingKey={actionLoading} /> ))} {(!deviceProfiles?.history || deviceProfiles.history.length === 0) && !deviceProfiles?.baseline && (
{t('accounts.device_fingerprint_dialog.no_history')}
)}
)}
{confirmProfile && confirmType && ( { if (actionLoading) return; setConfirmProfile(null); setConfirmType(null); }} onConfirm={confirmType === 'generate' ? handleConfirmGenerate : handleRestoreOriginal} loading={!!actionLoading} /> )}
, document.body ); } interface HistoryRowProps { id?: string; label: string; createdAt: number; profile: DeviceProfile; onRestore: () => void; onDelete?: () => void; isCurrent?: boolean; loadingKey?: string | null; } function HistoryRow({ id, label, createdAt, profile, onRestore, onDelete, isCurrent, loadingKey }: HistoryRowProps) { const { t } = useTranslation(); const key = id || label; return (
{label}{isCurrent && {t('accounts.device_fingerprint_dialog.current')}}
{createdAt > 0 &&
{new Date(createdAt * 1000).toLocaleString()}
}
machineId: {profile.machine_id}
macMachineId: {profile.mac_machine_id}
devDeviceId: {profile.dev_device_id}
sqmId: {profile.sqm_id}
{!isCurrent && onDelete && ( )}
); } function ConfirmDialog({ profile, type, onConfirm, onCancel, loading }: { profile: DeviceProfile; type: 'generate' | 'restoreOriginal'; onConfirm: () => void; onCancel: () => void; loading?: boolean }) { const { t } = useTranslation(); const title = type === 'generate' ? t('accounts.device_fingerprint_dialog.confirm_generate_title') : t('accounts.device_fingerprint_dialog.confirm_restore_title'); const desc = type === 'generate' ? t('accounts.device_fingerprint_dialog.confirm_generate_desc') : t('accounts.device_fingerprint_dialog.confirm_restore_desc'); return createPortal(

{title}

{desc}

machineId: {profile.machine_id}
macMachineId: {profile.mac_machine_id}
devDeviceId: {profile.dev_device_id}
sqmId: {profile.sqm_id}
, document.body ); }