File size: 2,829 Bytes
8fcabb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

import React, { useCallback } from 'react';
import type { BluetoothDevice, Action } from '../types.ts';
import { DeviceStatus } from '../types.ts';
import { DeviceCard } from './DeviceCard.tsx';
import { ScanIcon } from './icons.tsx';

interface DeviceScannerProps {
  devices: BluetoothDevice[];
  onScan: () => void;
  dispatch: React.Dispatch<Action>;
  isScanning: boolean;
}

export const DeviceScanner: React.FC<DeviceScannerProps> = ({ devices, onScan, dispatch, isScanning }) => {
  const handleConnect = useCallback((id: string) => {
    dispatch({ type: 'UPDATE_STATUS', payload: { id, status: DeviceStatus.Connecting } });
    setTimeout(() => {
      const randomLatency = Math.floor(Math.random() * 50) + 5;
      dispatch({ type: 'UPDATE_STATUS', payload: { id, status: DeviceStatus.Connected, latency: randomLatency } });
    }, 1500);
  }, [dispatch]);

  return (
    <div className="bg-slate-800/50 rounded-lg p-6 shadow-lg h-full flex flex-col">
      <div className="flex justify-between items-center mb-4">
        <h2 className="text-xl font-semibold text-white">Available Devices</h2>
        <button
          onClick={onScan}
          disabled={isScanning}
          className="flex items-center space-x-2 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:bg-slate-600 disabled:cursor-not-allowed transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-slate-900"
          aria-label={isScanning ? 'Scanning for devices' : 'Scan for new devices'}
        >
          {isScanning ? (
            <svg className="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
              <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
              <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
            </svg>
          ) : (
            <ScanIcon className="h-5 w-5" />
          )}
          <span>{isScanning ? 'Scanning...' : 'Scan'}</span>
        </button>
      </div>
      <div className="flex-grow overflow-y-auto pr-2 -mr-2 space-y-3">
        {isScanning && devices.length === 0 && (
          <div className="text-center py-10 text-slate-400">
            <p>Searching for nearby devices...</p>
          </div>
        )}
        {!isScanning && devices.length === 0 && (
          <div className="text-center py-10 text-slate-400">
            <p>No devices found. Try scanning again.</p>
          </div>
        )}
        {devices.map(device => (
          <DeviceCard key={device.id} device={device} onConnect={handleConnect} />
        ))}
      </div>
    </div>
  );
};