import { useEffect, useRef, useState } from 'react'; import { ShieldAlert, HelpCircle } from 'lucide-react'; import { createPortal } from 'react-dom'; import { api } from '~renderer/utils/api'; import { type ModsStatus, type UpdaterStatus } from '~main/types'; import { useT } from '~renderer/i18n'; import TextButton from './styled/TextButton'; const DETECTION = 'Trojan:Win32/Vigorf.A'; const AntivirusModal = () => { const t = useT(); const [status, setStatus] = useState(); api.mods.observe.useSubscription(undefined, { onData: setStatus }); // re-scan for AV blocks once the updater settles (not mid-download); catches an aborted // download or a file quarantined after the fact const [updateState, setUpdateState] = useState(); api.updater.observe.useSubscription(undefined, { onData: s => setUpdateState(s?.state) }); const settled = !!updateState && updateState !== 'verifying' && updateState !== 'updating'; const { data: quarantined, refetch: refetchQuarantined } = api.general.antivirusBlocks.useQuery(undefined, { enabled: false, refetchOnWindowFocus: false, staleTime: Infinity }); useEffect(() => { if (settled) refetchQuarantined(); }, [updateState, settled, refetchQuarantined]); const addExclusion = api.general.addDefenderExclusion.useMutation(); const blocked = [ ...new Set([ ...(quarantined ?? []), ...(status?.mods ?? []) .filter(m => m.state === 'error' && m.error?.includes('Defender')) .map(m => m.name) ]) ]; const blockedKey = blocked.join(','); const dialogRef = useRef(null); const [view, setView] = useState<'av' | 'why' | null>(null); useEffect(() => { if (view) { // showModal() throws (and crashes to the error screen) if already open, e.g. the av<->why switch if (!dialogRef.current?.open) dialogRef.current?.showModal(); (document.activeElement as HTMLElement | null)?.blur(); } else dialogRef.current?.close(); }, [view]); useEffect(() => { if (blockedKey) setView('av'); }, [blockedKey]); // The settings dialog opens the explainer straight from its antivirus button. useEffect(() => { const open = () => setView('why'); window.addEventListener('av-help', open); return () => window.removeEventListener('av-help', open); }, []); const names = blockedKey ? blockedKey.split(',') : []; return createPortal( setView(null)} className="h-full w-full items-center justify-center bg-[transparent] backdrop:backdrop-blur-sm [&[open]]:flex" > {view === 'av' && (

{t('av.blockedTitle')}

{names.length === 1 ? t('av.blockedOne') : t('av.blockedMany')}

    {names.map(n => (
  • • {n}
  • ))}

{t('av.blockedExplain')}

setView('why')} className="self-start text-yellow hocus:!text-yellow" > {t('av.whyHappening')} addExclusion.mutate()} className="self-start text-orange" > {t('av.allowThrough')} {addExclusion.data?.ok === true && ( {t('av.addedRetry')} )} {addExclusion.data?.ok === false && ( {addExclusion.data.error} )}
setView(null)} className="text-green"> {t('av.close')}
)} {view === 'why' && (

{t('av.whyTitle')}

{t('av.whyIntro', { detection: DETECTION })}{' '} {t('av.falsePositive')}.

{t('av.whatSetsItOff')}

{t('av.whatSetsItOffIntro')}

  • {t('av.dllInjection')}:{' '} {t('av.dllInjectionText')}
  • {t('av.exePatching')}:{' '} {t('av.exePatchingText')}

{t('av.heuristicNote')}

{t('av.whatModsDo')}

  • VanillaFixes:{' '} {t('av.modVanillaFixes')}
  • nampower:{' '} {t('av.modNampower')}
  • SuperWoW and UnitXP:{' '} {t('av.modSuperWowUnitXp')}
  • DXVK:{' '} {t('av.modDxvk')}

{t('av.howToVerify')}

{t('av.howToVerifyText', { detection: DETECTION })}

{t('av.whatAllowDoesTitle')}

{t('av.whatAllowDoesIntro')}{' '} Add-MpPreference{' '} {t('av.whatAllowDoesIntroAfter')}

  • {t('av.exclusionFoldersBefore')}{' '} {t('av.gameFolder')}{' '} {t('av.exclusionFoldersAnd')}{' '} {t('av.launcherFolder')} {t('av.exclusionFoldersAfter')}
  • WoW.exe{' '} {t('av.exclusionExesAnd')}{' '} VanillaFixes.exe{' '} {t('av.exclusionExesAfter')}

{t('av.whatAllowDoesOutro')}

{names.length > 0 && ( setView('av')} className="text-blueGray" > {t('av.back')} )} setView(null)} className="text-green"> {t('av.close')}
)}
, document.body ); }; export default AntivirusModal;