Last active
October 31, 2024 07:31
-
-
Save diachedelic/0d60233dab3dcae3215da8a4dfdcd434 to your computer and use it in GitHub Desktop.
Deep link to a native app from a browser, with a fallback
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function DeepLinker(options) { | |
if (!options) { | |
throw new Error('no options') | |
} | |
var hasFocus = true; | |
var didHide = false; | |
// window is blurred when dialogs are shown | |
function onBlur() { | |
hasFocus = false; | |
}; | |
// document is hidden when native app is shown or browser is backgrounded | |
function onVisibilityChange(e) { | |
if (e.target.visibilityState === 'hidden') { | |
didHide = true; | |
} | |
}; | |
// window is focused when dialogs are hidden, or browser comes into view | |
function onFocus() { | |
if (didHide) { | |
if (options.onReturn) { | |
options.onReturn(); | |
} | |
didHide = false; // reset | |
} else { | |
// ignore duplicate focus event when returning from native app on | |
// iOS Safari 13.3+ | |
if (!hasFocus && options.onFallback) { | |
// wait for app switch transition to fully complete - only then is | |
// 'visibilitychange' fired | |
setTimeout(function() { | |
// if browser was not hidden, the deep link failed | |
if (!didHide) { | |
options.onFallback(); | |
} | |
}, 1000); | |
} | |
} | |
hasFocus = true; | |
}; | |
// add/remove event listeners | |
// `mode` can be "add" or "remove" | |
function bindEvents(mode) { | |
[ | |
[window, 'blur', onBlur], | |
[document, 'visibilitychange', onVisibilityChange], | |
[window, 'focus', onFocus], | |
].forEach(function(conf) { | |
conf[0][mode + 'EventListener'](conf[1], conf[2]); | |
}); | |
} | |
// add event listeners | |
bindEvents('add'); | |
// expose public API | |
this.destroy = bindEvents.bind(null, 'remove'); | |
this.openURL = function(url) { | |
// it can take a while for the dialog to appear | |
var dialogTimeout = 500; | |
setTimeout(function() { | |
if (hasFocus && options.onIgnored) { | |
options.onIgnored(); | |
} | |
}, dialogTimeout); | |
window.location = url; | |
}; | |
} | |
/* usage */ | |
var url = 'fb://profile/240995729348595'; | |
var badURL = 'lksadjgajsdhfaskd://slkdfs'; | |
var linker = new DeepLinker({ | |
onIgnored: function() { | |
console.log('browser failed to respond to the deep link'); | |
}, | |
onFallback: function() { | |
console.log('dialog hidden or user returned to tab'); | |
}, | |
onReturn: function() { | |
console.log('user returned to the page from the native app'); | |
}, | |
}); | |
linker.openURL(url); |
Thank you king
working on iOS 17.0.3, had to tweak the timeout values a bit but otherwise works beautifully, ty.
behaviour in safari is if user does not have the app installed, onFallback is triggered.
in chrome, onIgnored is triggered. onReturn fires as expected in both browsers
try this.
function openDeepLink(url: string, options?: Partial<{ onOpen?: () => void; onFail?: () => void; waitTime?: number }>) {
let timeout: NodeJS.Timeout;
let interval: NodeJS.Timer;
let visible: DocumentVisibilityState = 'visible';
const handleOpen = () => {
window.removeEventListener('visibilitychange', () => true);
options?.onOpen?.();
};
const handleResponse = () => {
if (visible === 'visible') return options?.onFail?.();
clearInterval(interval);
handleOpen();
};
try {
window.addEventListener('visibilitychange', (e) => (visible = (e.target as Document)?.visibilityState));
timeout = setTimeout(handleResponse, options?.waitTime || 5000);
interval = setInterval(() => {
if (visible === 'hidden') {
clearTimeout(timeout);
handleResponse();
}
}, options?.waitTime || 5000);
window.location.href = url;
} catch (error) {
options?.onFail?.();
}
}
openDeepLink("fb://profile/100004452732850", {onOpen: console.log, onFail: console.error});
Tested on last version of Firefox 124, Android 13. Not working because even if a dialog appears to redirect to the app, the pages doesn't loose focus.
Can it be fixed?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm not the type to leave comments. Thank you so much. God bless you