Creates a dedicated page to handle browsers that don't support Web Bluetooth API. The page detects the user's browser and provides appropriate guidance. Features: - Automatic browser and platform detection - User-friendly messages for Safari, Firefox, iOS - Recommended browsers with download links - Mobile app alternative suggestion - Technical details section (expandable) - Responsive design matching existing pages Browser detection: - Chrome 70+ (supported) - Edge 79+ (supported) - Opera 57+ (supported) - Safari (not supported - Apple limitation) - Firefox (not supported - privacy concerns) - iOS (not supported - system restrictions) Files: - web-pages/unsupported-browser.html - Standalone page with inline detection - __tests__/web-pages/unsupported-browser.test.ts - Comprehensive test coverage All tests pass (17/17).
460 lines
14 KiB
HTML
460 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Browser Not Supported - WellNuo</title>
|
|
<link rel="icon" type="image/png" href="/favicon.png">
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8ec 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
.container {
|
|
background: white;
|
|
border-radius: 20px;
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
|
|
max-width: 600px;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
}
|
|
.header {
|
|
background: #f8f9fa;
|
|
padding: 32px;
|
|
text-align: center;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
.logo-img { width: 160px; height: auto; }
|
|
.content { padding: 32px; }
|
|
|
|
.warning-icon {
|
|
width: 80px;
|
|
height: 80px;
|
|
background: #fff3e0;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto 24px;
|
|
}
|
|
.warning-icon svg { width: 40px; height: 40px; color: #f57c00; }
|
|
|
|
.title {
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 12px;
|
|
text-align: center;
|
|
}
|
|
.subtitle {
|
|
color: #666;
|
|
font-size: 15px;
|
|
line-height: 1.6;
|
|
text-align: center;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.info-box {
|
|
background: #f0f7ff;
|
|
border-radius: 12px;
|
|
padding: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
.info-title {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 8px;
|
|
}
|
|
.info-text {
|
|
font-size: 14px;
|
|
color: #555;
|
|
line-height: 1.5;
|
|
}
|
|
.browser-info {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 13px;
|
|
color: #666;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.browser-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
gap: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
.browser-card {
|
|
background: #f8f9fa;
|
|
border: 2px solid #e0e0e0;
|
|
border-radius: 12px;
|
|
padding: 16px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
transition: all 0.2s;
|
|
}
|
|
.browser-card:hover {
|
|
border-color: #4A90D9;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(74, 144, 217, 0.2);
|
|
}
|
|
.browser-logo {
|
|
font-size: 32px;
|
|
margin-bottom: 8px;
|
|
}
|
|
.browser-name {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 4px;
|
|
}
|
|
.browser-version {
|
|
font-size: 12px;
|
|
color: #666;
|
|
}
|
|
|
|
.divider {
|
|
border: 0;
|
|
border-top: 1px solid #e0e0e0;
|
|
margin: 24px 0;
|
|
}
|
|
|
|
.mobile-section {
|
|
background: #f5f7fa;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
}
|
|
.mobile-title {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 12px;
|
|
text-align: center;
|
|
}
|
|
.mobile-text {
|
|
font-size: 14px;
|
|
color: #666;
|
|
line-height: 1.5;
|
|
text-align: center;
|
|
margin-bottom: 16px;
|
|
}
|
|
.mobile-link {
|
|
display: block;
|
|
background: linear-gradient(135deg, #4A90D9 0%, #357ABD 100%);
|
|
color: white;
|
|
padding: 12px 24px;
|
|
border-radius: 10px;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
text-align: center;
|
|
transition: all 0.2s;
|
|
}
|
|
.mobile-link:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(74, 144, 217, 0.4);
|
|
}
|
|
|
|
.technical-details {
|
|
margin-top: 24px;
|
|
padding-top: 24px;
|
|
border-top: 1px solid #e0e0e0;
|
|
}
|
|
.details-toggle {
|
|
background: none;
|
|
border: none;
|
|
color: #4A90D9;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
text-decoration: underline;
|
|
padding: 0;
|
|
}
|
|
.details-content {
|
|
display: none;
|
|
margin-top: 12px;
|
|
font-size: 13px;
|
|
color: #666;
|
|
line-height: 1.6;
|
|
}
|
|
.details-content.show {
|
|
display: block;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<img src="/logo.png" alt="WellNuo" class="logo-img">
|
|
</div>
|
|
|
|
<div class="content">
|
|
<div class="warning-icon">
|
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z">
|
|
</path>
|
|
</svg>
|
|
</div>
|
|
|
|
<h1 class="title">Browser Not Supported</h1>
|
|
<p class="subtitle" id="unsupportedMessage">
|
|
WellNuo Web requires a browser that supports Web Bluetooth API for connecting to health sensors.
|
|
</p>
|
|
|
|
<div class="info-box">
|
|
<div class="info-title">Your Browser</div>
|
|
<div class="info-text" id="browserDetails">
|
|
Detecting browser...
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-title">Recommended Browsers</div>
|
|
<div class="browser-grid" id="browsersGrid">
|
|
<!-- Browsers will be inserted here -->
|
|
</div>
|
|
|
|
<hr class="divider">
|
|
|
|
<div class="mobile-section">
|
|
<div class="mobile-title">Prefer Mobile?</div>
|
|
<p class="mobile-text">
|
|
Download the WellNuo mobile app for the best experience on your smartphone or tablet.
|
|
</p>
|
|
<a href="https://wellnuo.com" class="mobile-link" target="_blank">
|
|
Visit WellNuo.com
|
|
</a>
|
|
</div>
|
|
|
|
<div class="technical-details">
|
|
<button class="details-toggle" onclick="toggleDetails()">
|
|
Why is my browser not supported?
|
|
</button>
|
|
<div class="details-content" id="technicalDetails">
|
|
<p><strong>Technical Details:</strong></p>
|
|
<p id="technicalReason">Loading...</p>
|
|
<br>
|
|
<p><strong>Web Bluetooth Requirements:</strong></p>
|
|
<ul style="margin-left: 20px; margin-top: 8px;">
|
|
<li>HTTPS connection (required for security)</li>
|
|
<li>Bluetooth hardware enabled on your device</li>
|
|
<li>Operating System: Windows 10+, macOS, or Linux</li>
|
|
<li>Browser: Chrome 70+, Edge 79+, or Opera 57+</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Browser detection logic (copied from web/lib/browserCheck.ts)
|
|
function detectBrowser() {
|
|
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
return { name: 'Unknown', version: '0' };
|
|
}
|
|
|
|
const userAgent = navigator.userAgent;
|
|
let name = 'Unknown';
|
|
let version = '0';
|
|
|
|
// Chrome
|
|
if (/Chrome\/(\d+)/.test(userAgent) && !/Edg/.test(userAgent) && !/OPR/.test(userAgent)) {
|
|
name = 'Chrome';
|
|
const match = userAgent.match(/Chrome\/(\d+)/);
|
|
version = match ? match[1] : '0';
|
|
}
|
|
// Edge
|
|
else if (/Edg\/(\d+)/.test(userAgent)) {
|
|
name = 'Edge';
|
|
const match = userAgent.match(/Edg\/(\d+)/);
|
|
version = match ? match[1] : '0';
|
|
}
|
|
// Opera
|
|
else if (/OPR\/(\d+)/.test(userAgent)) {
|
|
name = 'Opera';
|
|
const match = userAgent.match(/OPR\/(\d+)/);
|
|
version = match ? match[1] : '0';
|
|
}
|
|
// Safari
|
|
else if (/Safari\/(\d+)/.test(userAgent) && !/Chrome/.test(userAgent)) {
|
|
name = 'Safari';
|
|
const match = userAgent.match(/Version\/(\d+)/);
|
|
version = match ? match[1] : '0';
|
|
}
|
|
// Firefox
|
|
else if (/Firefox\/(\d+)/.test(userAgent)) {
|
|
name = 'Firefox';
|
|
const match = userAgent.match(/Firefox\/(\d+)/);
|
|
version = match ? match[1] : '0';
|
|
}
|
|
|
|
return { name, version };
|
|
}
|
|
|
|
function detectPlatform() {
|
|
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
return 'Unknown';
|
|
}
|
|
|
|
const userAgent = navigator.userAgent;
|
|
const platform = navigator.platform;
|
|
|
|
if (/Win/.test(platform)) return 'Windows';
|
|
if (/Mac/.test(platform)) return 'macOS';
|
|
if (/iPhone|iPad|iPod/.test(userAgent)) return 'iOS';
|
|
if (/Android/.test(userAgent)) return 'Android';
|
|
if (/Linux/.test(platform)) return 'Linux';
|
|
|
|
return 'Unknown';
|
|
}
|
|
|
|
function hasWebBluetoothAPI() {
|
|
return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
|
|
}
|
|
|
|
function getUnsupportedMessage(browserInfo) {
|
|
const { name, platform, hasWebBluetooth } = browserInfo;
|
|
|
|
if (platform === 'iOS') {
|
|
return 'Web Bluetooth is not supported on iOS due to system limitations. Please use a desktop browser or download our mobile app.';
|
|
}
|
|
|
|
if (name === 'Safari') {
|
|
return 'Safari does not support Web Bluetooth API. Please use Chrome, Edge, or Opera instead.';
|
|
}
|
|
|
|
if (name === 'Firefox') {
|
|
return 'Firefox does not support Web Bluetooth API due to privacy concerns. Please use Chrome, Edge, or Opera instead.';
|
|
}
|
|
|
|
if (!hasWebBluetooth) {
|
|
return 'Your browser does not support Web Bluetooth API. Please update to a newer version or try Chrome, Edge, or Opera.';
|
|
}
|
|
|
|
return 'Your browser version may be outdated. Please update to the latest version or try Chrome, Edge, or Opera.';
|
|
}
|
|
|
|
function getTechnicalReason(browserInfo) {
|
|
const { name, platform, hasWebBluetooth, version } = browserInfo;
|
|
|
|
if (platform === 'iOS') {
|
|
return 'Apple has not implemented Web Bluetooth API on iOS for security reasons. All browsers on iOS (including Chrome) use Safari\'s WebKit engine, which lacks Bluetooth support.';
|
|
}
|
|
|
|
if (name === 'Safari') {
|
|
return 'Apple has not implemented Web Bluetooth API in Safari. This is a design decision by Apple, not a technical limitation.';
|
|
}
|
|
|
|
if (name === 'Firefox') {
|
|
return 'Mozilla has chosen not to implement Web Bluetooth API due to privacy and security concerns. There are no current plans to add support.';
|
|
}
|
|
|
|
if (!hasWebBluetooth) {
|
|
return `Your browser (${name} ${version}) does not have the Web Bluetooth API available. This could be due to an outdated version or platform restrictions.`;
|
|
}
|
|
|
|
const versionNum = parseInt(version, 10);
|
|
if (name === 'Chrome' && versionNum < 70) {
|
|
return `Your Chrome version (${version}) is too old. Web Bluetooth API requires Chrome 70 or newer.`;
|
|
}
|
|
if (name === 'Edge' && versionNum < 79) {
|
|
return `Your Edge version (${version}) is too old. Web Bluetooth API requires Edge 79 or newer.`;
|
|
}
|
|
if (name === 'Opera' && versionNum < 57) {
|
|
return `Your Opera version (${version}) is too old. Web Bluetooth API requires Opera 57 or newer.`;
|
|
}
|
|
|
|
return `Your browser (${name} ${version} on ${platform}) may not fully support the Web Bluetooth features required by WellNuo.`;
|
|
}
|
|
|
|
const browserLogos = {
|
|
'Google Chrome': '🌐',
|
|
'Microsoft Edge': '🔷',
|
|
'Opera': '⭕'
|
|
};
|
|
|
|
const recommendedBrowsers = [
|
|
{
|
|
name: 'Google Chrome',
|
|
minVersion: '70',
|
|
downloadUrl: 'https://www.google.com/chrome/',
|
|
},
|
|
{
|
|
name: 'Microsoft Edge',
|
|
minVersion: '79',
|
|
downloadUrl: 'https://www.microsoft.com/edge',
|
|
},
|
|
{
|
|
name: 'Opera',
|
|
minVersion: '57',
|
|
downloadUrl: 'https://www.opera.com/',
|
|
},
|
|
];
|
|
|
|
// Initialize page
|
|
function initPage() {
|
|
const browser = detectBrowser();
|
|
const platform = detectPlatform();
|
|
const hasWebBluetooth = hasWebBluetoothAPI();
|
|
|
|
const browserInfo = {
|
|
name: browser.name,
|
|
version: browser.version,
|
|
platform,
|
|
hasWebBluetooth,
|
|
};
|
|
|
|
// Update message
|
|
const message = getUnsupportedMessage(browserInfo);
|
|
document.getElementById('unsupportedMessage').textContent = message;
|
|
|
|
// Update browser details
|
|
const detailsText = `${browser.name} ${browser.version} on ${platform}`;
|
|
const webBluetoothStatus = hasWebBluetooth ? 'Available ✓' : 'Not Available ✗';
|
|
document.getElementById('browserDetails').innerHTML = `
|
|
<strong>${detailsText}</strong>
|
|
<div class="browser-info">Web Bluetooth: ${webBluetoothStatus}</div>
|
|
`;
|
|
|
|
// Update technical reason
|
|
const technicalReason = getTechnicalReason(browserInfo);
|
|
document.getElementById('technicalReason').textContent = technicalReason;
|
|
|
|
// Render browser cards
|
|
const grid = document.getElementById('browsersGrid');
|
|
grid.innerHTML = recommendedBrowsers.map(browser => `
|
|
<a href="${browser.downloadUrl}" class="browser-card" target="_blank" rel="noopener noreferrer">
|
|
<div class="browser-logo">${browserLogos[browser.name]}</div>
|
|
<div class="browser-name">${browser.name}</div>
|
|
<div class="browser-version">Version ${browser.minVersion}+</div>
|
|
</a>
|
|
`).join('');
|
|
}
|
|
|
|
function toggleDetails() {
|
|
const details = document.getElementById('technicalDetails');
|
|
details.classList.toggle('show');
|
|
|
|
const button = document.querySelector('.details-toggle');
|
|
button.textContent = details.classList.contains('show')
|
|
? 'Hide technical details'
|
|
: 'Why is my browser not supported?';
|
|
}
|
|
|
|
// Run on load
|
|
initPage();
|
|
</script>
|
|
</body>
|
|
</html>
|