- Add webBluetooth.ts with browser detection and compatibility checks - Add WebBLEManager implementing IBLEManager for Web Bluetooth API - Add BrowserNotSupported component showing clear error for Safari/Firefox - Update services/ble/index.ts to use WebBLEManager on web platform - Add comprehensive tests for browser detection and WebBLEManager Works in Chrome/Edge/Opera, shows user-friendly error in unsupported browsers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
174 lines
5.2 KiB
TypeScript
174 lines
5.2 KiB
TypeScript
// Web Bluetooth browser compatibility utilities
|
|
|
|
/**
|
|
* Web Bluetooth support status
|
|
*/
|
|
export interface WebBluetoothSupport {
|
|
supported: boolean;
|
|
browserName: string;
|
|
browserVersion: string;
|
|
reason?: 'unsupported_browser' | 'insecure_context' | 'api_unavailable';
|
|
helpUrl?: string;
|
|
}
|
|
|
|
/**
|
|
* Supported browsers for Web Bluetooth
|
|
* Chrome/Edge/Opera on desktop and Android support Web Bluetooth
|
|
* Safari and Firefox do NOT support Web Bluetooth
|
|
*/
|
|
export const SUPPORTED_BROWSERS = ['Chrome', 'Edge', 'Opera', 'Samsung Internet'];
|
|
|
|
/**
|
|
* URLs for browser help pages
|
|
*/
|
|
export const BROWSER_HELP_URLS: Record<string, string> = {
|
|
Chrome: 'https://www.google.com/chrome/',
|
|
Edge: 'https://www.microsoft.com/edge',
|
|
Opera: 'https://www.opera.com/',
|
|
'Samsung Internet': 'https://www.samsung.com/us/support/owners/app/samsung-internet',
|
|
};
|
|
|
|
/**
|
|
* Detect the current browser name and version
|
|
*/
|
|
export function detectBrowser(): { name: string; version: string } {
|
|
if (typeof navigator === 'undefined') {
|
|
return { name: 'Unknown', version: '0' };
|
|
}
|
|
|
|
const userAgent = navigator.userAgent;
|
|
let browserName = 'Unknown';
|
|
let browserVersion = '0';
|
|
|
|
// Edge (must be checked before Chrome as Edge contains "Chrome" in UA)
|
|
if (userAgent.includes('Edg/')) {
|
|
browserName = 'Edge';
|
|
const match = userAgent.match(/Edg\/(\d+)/);
|
|
browserVersion = match?.[1] || '0';
|
|
}
|
|
// Opera (must be checked before Chrome as Opera contains "Chrome" in UA)
|
|
else if (userAgent.includes('OPR/') || userAgent.includes('Opera')) {
|
|
browserName = 'Opera';
|
|
const match = userAgent.match(/(?:OPR|Opera)\/(\d+)/);
|
|
browserVersion = match?.[1] || '0';
|
|
}
|
|
// Samsung Internet (must be checked before Chrome)
|
|
else if (userAgent.includes('SamsungBrowser')) {
|
|
browserName = 'Samsung Internet';
|
|
const match = userAgent.match(/SamsungBrowser\/(\d+)/);
|
|
browserVersion = match?.[1] || '0';
|
|
}
|
|
// Chrome
|
|
else if (userAgent.includes('Chrome/')) {
|
|
browserName = 'Chrome';
|
|
const match = userAgent.match(/Chrome\/(\d+)/);
|
|
browserVersion = match?.[1] || '0';
|
|
}
|
|
// Safari (must be checked after Chrome-based browsers)
|
|
else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
|
|
browserName = 'Safari';
|
|
const match = userAgent.match(/Version\/(\d+)/);
|
|
browserVersion = match?.[1] || '0';
|
|
}
|
|
// Firefox
|
|
else if (userAgent.includes('Firefox/')) {
|
|
browserName = 'Firefox';
|
|
const match = userAgent.match(/Firefox\/(\d+)/);
|
|
browserVersion = match?.[1] || '0';
|
|
}
|
|
|
|
return { name: browserName, version: browserVersion };
|
|
}
|
|
|
|
/**
|
|
* Check if the current browser supports Web Bluetooth API
|
|
*/
|
|
export function checkWebBluetoothSupport(): WebBluetoothSupport {
|
|
const browser = detectBrowser();
|
|
|
|
// Check if we're in a secure context (HTTPS or localhost)
|
|
if (typeof window !== 'undefined' && !window.isSecureContext) {
|
|
return {
|
|
supported: false,
|
|
browserName: browser.name,
|
|
browserVersion: browser.version,
|
|
reason: 'insecure_context',
|
|
};
|
|
}
|
|
|
|
// Check if the Web Bluetooth API is available
|
|
if (typeof navigator === 'undefined' || !('bluetooth' in navigator)) {
|
|
// Determine reason based on browser
|
|
const isKnownUnsupportedBrowser =
|
|
browser.name === 'Safari' ||
|
|
browser.name === 'Firefox';
|
|
|
|
return {
|
|
supported: false,
|
|
browserName: browser.name,
|
|
browserVersion: browser.version,
|
|
reason: isKnownUnsupportedBrowser ? 'unsupported_browser' : 'api_unavailable',
|
|
helpUrl: BROWSER_HELP_URLS.Chrome, // Suggest Chrome as alternative
|
|
};
|
|
}
|
|
|
|
return {
|
|
supported: true,
|
|
browserName: browser.name,
|
|
browserVersion: browser.version,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get a user-friendly error message for unsupported browsers
|
|
*/
|
|
export function getUnsupportedBrowserMessage(support: WebBluetoothSupport): {
|
|
title: string;
|
|
message: string;
|
|
suggestion: string;
|
|
} {
|
|
switch (support.reason) {
|
|
case 'unsupported_browser':
|
|
return {
|
|
title: 'Browser Not Supported',
|
|
message: `${support.browserName} does not support Web Bluetooth, which is required to connect to WellNuo sensors.`,
|
|
suggestion: 'Please use Chrome, Edge, or Opera browser to set up your sensors.',
|
|
};
|
|
|
|
case 'insecure_context':
|
|
return {
|
|
title: 'Secure Connection Required',
|
|
message: 'Web Bluetooth requires a secure connection (HTTPS).',
|
|
suggestion: 'Please access this page using HTTPS or localhost.',
|
|
};
|
|
|
|
case 'api_unavailable':
|
|
return {
|
|
title: 'Bluetooth Not Available',
|
|
message: 'Web Bluetooth API is not available in this browser.',
|
|
suggestion: 'Please use Chrome, Edge, or Opera browser to set up your sensors.',
|
|
};
|
|
|
|
default:
|
|
return {
|
|
title: 'Bluetooth Not Available',
|
|
message: 'Could not access Bluetooth functionality.',
|
|
suggestion: 'Please use Chrome, Edge, or Opera browser.',
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Type guard to check if we're running in a web browser
|
|
*/
|
|
export function isWebPlatform(): boolean {
|
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
}
|
|
|
|
/**
|
|
* Type guard to check if Web Bluetooth is available
|
|
*/
|
|
export function hasWebBluetooth(): boolean {
|
|
return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
|
|
}
|