fix: avoid web fallback after app handoff
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 11s
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 11s
This commit is contained in:
103
app.js
103
app.js
@@ -60,11 +60,87 @@ function isMobileDevice() {
|
|||||||
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMobileUrl(service) {
|
function getMobilePlatform() {
|
||||||
|
const userAgent = navigator.userAgent || '';
|
||||||
|
if (/Android/i.test(userAgent)) return 'android';
|
||||||
|
if (/iPhone|iPad|iPod/i.test(userAgent)) return 'ios';
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMobileLaunch(service) {
|
||||||
if (service.id === 'drive') {
|
if (service.id === 'drive') {
|
||||||
return 'nextcloud://openFiles';
|
const fallbackUrl = service.url;
|
||||||
|
if (getMobilePlatform() === 'android') {
|
||||||
|
return {
|
||||||
|
url: `intent://openFiles#Intent;scheme=nextcloud;package=com.nextcloud.client;S.browser_fallback_url=${encodeURIComponent(fallbackUrl)};end`,
|
||||||
|
fallbackUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
url: 'nextcloud://openFiles',
|
||||||
|
fallbackUrl
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return service.mobileUrl;
|
if (!service.mobileUrl) return null;
|
||||||
|
return {
|
||||||
|
url: service.mobileUrl,
|
||||||
|
fallbackUrl: service.url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMobileLaunch(url, fallbackUrl) {
|
||||||
|
let didLeavePage = false;
|
||||||
|
let fallbackTimer = null;
|
||||||
|
let cleanupTimer = null;
|
||||||
|
let launcherFrame = null;
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
window.removeEventListener('pagehide', markPageLeft);
|
||||||
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||||
|
if (fallbackTimer) window.clearTimeout(fallbackTimer);
|
||||||
|
if (cleanupTimer) window.clearTimeout(cleanupTimer);
|
||||||
|
if (launcherFrame) launcherFrame.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
const markPageLeft = () => {
|
||||||
|
didLeavePage = true;
|
||||||
|
cleanup();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (document.visibilityState === 'hidden') {
|
||||||
|
markPageLeft();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('pagehide', markPageLeft, { once: true });
|
||||||
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||||
|
|
||||||
|
fallbackTimer = window.setTimeout(() => {
|
||||||
|
cleanup();
|
||||||
|
if (!didLeavePage && document.visibilityState === 'visible') {
|
||||||
|
window.location.href = fallbackUrl;
|
||||||
|
}
|
||||||
|
}, 2200);
|
||||||
|
|
||||||
|
if (/^https?:\/\//i.test(url) || /^intent:/i.test(url)) {
|
||||||
|
window.location.href = url;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
launcherFrame = document.createElement('iframe');
|
||||||
|
launcherFrame.hidden = true;
|
||||||
|
launcherFrame.style.display = 'none';
|
||||||
|
launcherFrame.setAttribute('aria-hidden', 'true');
|
||||||
|
document.body.appendChild(launcherFrame);
|
||||||
|
launcherFrame.src = url;
|
||||||
|
|
||||||
|
cleanupTimer = window.setTimeout(() => {
|
||||||
|
if (launcherFrame && launcherFrame.parentNode) {
|
||||||
|
launcherFrame.remove();
|
||||||
|
launcherFrame = null;
|
||||||
|
}
|
||||||
|
}, 2600);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createServiceCard(service) {
|
function createServiceCard(service) {
|
||||||
@@ -92,28 +168,13 @@ function createServiceCard(service) {
|
|||||||
<span class="card-domain">${service.domain}</span>
|
<span class="card-domain">${service.domain}</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const mobileUrl = getMobileUrl(service);
|
const mobileLaunch = getMobileLaunch(service);
|
||||||
if (mobileUrl) {
|
if (mobileLaunch) {
|
||||||
card.addEventListener('click', (e) => {
|
card.addEventListener('click', (e) => {
|
||||||
if (!isMobileDevice()) return;
|
if (!isMobileDevice()) return;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
openMobileLaunch(mobileLaunch.url, mobileLaunch.fallbackUrl);
|
||||||
if (/^https?:\/\//i.test(mobileUrl)) {
|
|
||||||
window.location.href = mobileUrl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = Date.now();
|
|
||||||
// Try to open the native mobile app through its registered URL scheme.
|
|
||||||
window.location.href = mobileUrl;
|
|
||||||
|
|
||||||
// Web fallback if the app is not installed or the scheme is not handled.
|
|
||||||
setTimeout(() => {
|
|
||||||
if (Date.now() - start < 1500) {
|
|
||||||
window.open(service.url, '_blank');
|
|
||||||
}
|
|
||||||
}, 800);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,6 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/app.js?v=30" defer></script>
|
<script src="/app.js?v=31" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
4
sw.js
4
sw.js
@@ -1,10 +1,10 @@
|
|||||||
const CACHE_NAME = 'mischlabs-pwa-v13';
|
const CACHE_NAME = 'mischlabs-pwa-v14';
|
||||||
const APP_SHELL = [
|
const APP_SHELL = [
|
||||||
'/',
|
'/',
|
||||||
'/index.html',
|
'/index.html',
|
||||||
'/offline.html',
|
'/offline.html',
|
||||||
'/style.css?v=27',
|
'/style.css?v=27',
|
||||||
'/app.js?v=30',
|
'/app.js?v=31',
|
||||||
'/services.json?v=1',
|
'/services.json?v=1',
|
||||||
'/manifest.webmanifest?v=4',
|
'/manifest.webmanifest?v=4',
|
||||||
'/favicon.ico?v=4',
|
'/favicon.ico?v=4',
|
||||||
|
|||||||
Reference in New Issue
Block a user