import { DefaultButton, Dialog, Dropdown, IDropdownOption, PrimaryButton, ProgressIndicator, Stack, StackItem, TooltipHost } from '@fluentui/react'; import { Adb, AdbBackend } from '@yume-chan/adb'; import AdbWebUsbBackend, { AdbWebCredentialStore, AdbWebUsbBackendWatcher } from '@yume-chan/adb-backend-webusb'; import AdbWsBackend from '@yume-chan/adb-backend-ws'; import { observer } from 'mobx-react-lite'; import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { global, logger } from '../state'; import { CommonStackTokens } from '../utils'; import { ErrorDialogContext } from './error-dialog'; const DropdownStyles = { dropdown: { width: '100%' } }; const CredentialStore = new AdbWebCredentialStore(); function _Connect(): JSX.Element | null { const [supported, setSupported] = useState(true); const { show: showErrorDialog } = useContext(ErrorDialogContext); const [selectedBackend, setSelectedBackend] = useState(); const [connecting, setConnecting] = useState(false); const [usbBackendList, setUsbBackendList] = useState([]); const updateUsbBackendList = useCallback(async () => { const backendList: AdbBackend[] = await AdbWebUsbBackend.getDevices(); setUsbBackendList(backendList); return backendList; }, []); useEffect( () => { // Only run on client const supported = AdbWebUsbBackend.isSupported(); setSupported(supported); if (!supported) { showErrorDialog('Your browser does not support WebUSB standard, which is required for this site to work.\n\nLatest version of Google Chrome, Microsoft Edge, or other Chromium-based browsers are required.'); return; } updateUsbBackendList(); const watcher = new AdbWebUsbBackendWatcher(async (serial?: string) => { const list = await updateUsbBackendList(); if (serial) { setSelectedBackend(list.find(backend => backend.serial === serial)); return; } }); return () => watcher.dispose(); }, /* eslint-disable-next-line react-hooks/exhaustive-deps */ [] ); const [wsBackendList, setWsBackendList] = useState([]); useEffect(() => { const intervalId = setInterval(async () => { if (connecting || global.device) { return; } const wsBackend = new AdbWsBackend("ws://localhost:15555"); try { await wsBackend.connect(); setWsBackendList([wsBackend]); setSelectedBackend(wsBackend); } catch { setWsBackendList([]); } finally { await wsBackend.dispose(); } }, 5000); return () => { clearInterval(intervalId); }; }, [connecting]); const handleSelectedBackendChange = ( e: React.FormEvent, option?: IDropdownOption, ) => { setSelectedBackend(option?.data as AdbBackend); }; const requestAccess = useCallback(async () => { const backend = await AdbWebUsbBackend.requestDevice(); setSelectedBackend(backend); await updateUsbBackendList(); }, [updateUsbBackendList]); const connect = useCallback(async () => { try { if (selectedBackend) { const adb = new Adb(selectedBackend, logger.logger); try { setConnecting(true); await adb.connect(CredentialStore); global.setCurrent(adb); } catch (e) { adb.dispose(); throw e; } } } catch (e: any) { showErrorDialog(e.message); } finally { setConnecting(false); } }, [showErrorDialog, selectedBackend]); const disconnect = useCallback(async () => { try { await global.device!.dispose(); global.setCurrent(undefined); } catch (e: any) { showErrorDialog(e.message); } }, [showErrorDialog]); const backendList = useMemo( () => ([] as AdbBackend[]).concat(usbBackendList, wsBackendList), [usbBackendList, wsBackendList] ); const backendOptions = useMemo(() => { return backendList.map(backend => ({ key: backend.serial, text: `${backend.serial} ${backend.name ? `(${backend.name})` : ''}`, data: backend, })); }, [backendList]); useEffect(() => { setSelectedBackend(old => { if (old) { const current = backendList.find(backend => backend.serial === old.serial); if (current) { return current; } } return backendList.length ? backendList[0] : undefined; }); }, [backendList]); return ( {!global.device ? ( ) : ( )} ); }; export const Connect = observer(_Connect);