New mobile/ directory with Capacitor project: - Configurable server URL launcher (default: app.pedshub.com) - Android: foreground service + wake lock for background recording (AudioRecordingService preserved from existing TWA) - iOS: background audio mode + microphone permission - App ID: com.pedshub.scribe - Both platforms initialized and synced Existing android/ TWA project untouched — this is a separate project. Build: cd mobile && npx cap open android (or ios)
116 lines
3.8 KiB
JavaScript
116 lines
3.8 KiB
JavaScript
// PedScribe Mobile Launcher
|
|
// Handles configurable server URL and auto-redirect
|
|
|
|
(function() {
|
|
var STORAGE_KEY = 'pedscribe_server_url';
|
|
var DEFAULT_URL = 'https://app.pedshub.com';
|
|
|
|
var setupScreen = document.getElementById('setup-screen');
|
|
var connectingScreen = document.getElementById('connecting-screen');
|
|
var urlInput = document.getElementById('server-url');
|
|
var connectBtn = document.getElementById('btn-connect');
|
|
var changeBtn = document.getElementById('btn-change-server');
|
|
|
|
// Check for saved server URL
|
|
var savedUrl = localStorage.getItem(STORAGE_KEY);
|
|
|
|
if (savedUrl) {
|
|
// Auto-connect to saved server
|
|
showConnecting(savedUrl);
|
|
} else {
|
|
// Show setup screen
|
|
urlInput.value = DEFAULT_URL;
|
|
setupScreen.style.display = '';
|
|
connectingScreen.style.display = 'none';
|
|
}
|
|
|
|
// Connect button
|
|
connectBtn.addEventListener('click', function() {
|
|
var url = (urlInput.value || DEFAULT_URL).trim().replace(/\/+$/, '');
|
|
if (!url.startsWith('http')) url = 'https://' + url;
|
|
|
|
connectBtn.disabled = true;
|
|
connectBtn.textContent = 'Connecting...';
|
|
|
|
// Test the server is reachable
|
|
testServer(url, function(ok) {
|
|
if (ok) {
|
|
localStorage.setItem(STORAGE_KEY, url);
|
|
navigateToServer(url);
|
|
} else {
|
|
connectBtn.disabled = false;
|
|
connectBtn.textContent = 'Connect';
|
|
showError('Could not reach server. Check the URL and try again.');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Enter key
|
|
urlInput.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Enter') connectBtn.click();
|
|
});
|
|
|
|
// Change server button (from connecting screen)
|
|
changeBtn.addEventListener('click', function() {
|
|
localStorage.removeItem(STORAGE_KEY);
|
|
setupScreen.style.display = '';
|
|
connectingScreen.style.display = 'none';
|
|
urlInput.value = savedUrl || DEFAULT_URL;
|
|
urlInput.focus();
|
|
});
|
|
|
|
function showConnecting(url) {
|
|
setupScreen.style.display = 'none';
|
|
connectingScreen.style.display = '';
|
|
|
|
// Brief delay so the user sees the connecting screen
|
|
setTimeout(function() {
|
|
testServer(url, function(ok) {
|
|
if (ok) {
|
|
navigateToServer(url);
|
|
} else {
|
|
// Server not reachable — show setup screen
|
|
setupScreen.style.display = '';
|
|
connectingScreen.style.display = 'none';
|
|
urlInput.value = url;
|
|
showError('Server not reachable. Check your connection or change the URL.');
|
|
}
|
|
});
|
|
}, 500);
|
|
}
|
|
|
|
function testServer(url, callback) {
|
|
// Try to reach the health endpoint
|
|
var controller = new AbortController();
|
|
var timeout = setTimeout(function() { controller.abort(); }, 8000);
|
|
|
|
fetch(url + '/api/health', { signal: controller.signal, mode: 'no-cors' })
|
|
.then(function() { clearTimeout(timeout); callback(true); })
|
|
.catch(function() {
|
|
clearTimeout(timeout);
|
|
// no-cors mode always succeeds for reachable servers (opaque response)
|
|
// If it fails, the server is truly unreachable
|
|
// Try again with a simple image load as fallback
|
|
var img = new Image();
|
|
img.onload = function() { callback(true); };
|
|
img.onerror = function() { callback(true); }; // Even errors mean server responded
|
|
img.src = url + '/favicon.ico?t=' + Date.now();
|
|
setTimeout(function() { callback(false); }, 5000);
|
|
});
|
|
}
|
|
|
|
function navigateToServer(url) {
|
|
window.location.href = url;
|
|
}
|
|
|
|
function showError(msg) {
|
|
var existing = document.querySelector('.error-msg');
|
|
if (existing) existing.remove();
|
|
var div = document.createElement('div');
|
|
div.className = 'error-msg';
|
|
div.style.display = 'block';
|
|
div.textContent = msg;
|
|
connectBtn.parentNode.insertBefore(div, connectBtn.nextSibling);
|
|
setTimeout(function() { div.remove(); }, 8000);
|
|
}
|
|
})();
|