Let the "Open on" button choose a browser #8
@@ -95,11 +95,12 @@ which is the browser being escaped. The choice is kept in that browser's own
|
||||
`localStorage` — not a cookie and not synced, because "which browser is installed" is a
|
||||
fact about the device, not about you, and the phone's answer is not the Mac's.
|
||||
|
||||
The link in the page is always the plain `https://` address, and the scheme is swapped in
|
||||
by the script afterwards; with JavaScript off, or with the default left alone, nothing
|
||||
changes. Once a browser is chosen the button says which one, because a scheme for a
|
||||
browser that isn't installed opens nothing at all, and a silent tap needs an
|
||||
explanation. The URL underneath stays selectable either way.
|
||||
Until a browser is chosen there is no "Open on …" button at all — the plain address is
|
||||
the one thing it must not offer, since following it only comes back here. The markup
|
||||
carries that address anyway, hidden, and the script swaps in the scheme and reveals the
|
||||
button once there is somewhere to send it. So with JavaScript off, or with the default
|
||||
left alone, the copy button and the selectable URL are the whole of it, which is all
|
||||
that was ever load-bearing.
|
||||
|
||||
## How it works
|
||||
|
||||
|
||||
+8
-11
@@ -1,7 +1,7 @@
|
||||
// Progressive enhancement only. Without this file the page still shows the
|
||||
// media in a swipeable rail and the original URL as selectable text.
|
||||
|
||||
import { browserById, browsersFor, detectOs, openUrlFor } from './browsers.js';
|
||||
import { browsersFor, detectOs, openUrlFor } from './browsers.js';
|
||||
|
||||
const VIEW_KEY = 'antisocial:view';
|
||||
const BROWSER_KEY = 'antisocial:browser';
|
||||
@@ -48,24 +48,21 @@ function storedBrowser() {
|
||||
}
|
||||
}
|
||||
|
||||
// The markup carries the plain address, so the link still goes somewhere
|
||||
// with this file missing. Only once a browser has been chosen is the href
|
||||
// swapped for that browser's scheme -- and the name goes onto the button,
|
||||
// because a scheme for a browser that is not installed opens nothing and
|
||||
// the tap would otherwise be a silent no-op.
|
||||
// The markup carries the plain address, which is the one thing the button
|
||||
// must not offer: followed in the browser the rewrite rules are installed
|
||||
// in, it redirects straight back to this page. So the link ships hidden and
|
||||
// is only revealed once a browser has been picked to hand it to. With no
|
||||
// choice made -- or none this system has a scheme for -- the copy button
|
||||
// and the URL below it are the whole of it.
|
||||
function applyBrowser(id) {
|
||||
const os = detectOs();
|
||||
const label = browserById(id)?.label;
|
||||
|
||||
for (const link of document.querySelectorAll('.original__open')) {
|
||||
link.dataset.original ??= link.getAttribute('href');
|
||||
link.dataset.label ??= link.textContent.trim();
|
||||
|
||||
const href = openUrlFor(link.dataset.original, id, os);
|
||||
link.href = href;
|
||||
link.textContent = href === link.dataset.original
|
||||
? link.dataset.label
|
||||
: `${link.dataset.label} in ${label}`;
|
||||
link.hidden = href === link.dataset.original;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ export function browsersFor(os) {
|
||||
return BROWSERS.filter((browser) => browser.id === 'default' || browser[os]);
|
||||
}
|
||||
|
||||
export function browserById(id) {
|
||||
function browserById(id) {
|
||||
return BROWSERS.find((browser) => browser.id === id);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export function renderChallenge(challenge: Challenge, box: Box | undefined): str
|
||||
<footer class="post__foot">
|
||||
<div class="original">
|
||||
<button type="button" class="copy" id="give-up">Give up and show me the link</button>
|
||||
<a class="original__open" href="${challenge.originalUrl}" rel="noopener noreferrer nofollow" target="_blank">
|
||||
<a class="original__open" href="${challenge.originalUrl}" rel="noopener noreferrer nofollow" target="_blank" hidden>
|
||||
Open on ${challenge.platformLabel}
|
||||
</a>
|
||||
<p class="original__url"><code>${challenge.originalUrl}</code></p>
|
||||
|
||||
@@ -43,14 +43,19 @@ export function badge(platform: string, label: string): Raw {
|
||||
}
|
||||
|
||||
/** The copy control, plus the URL itself so it is always selectable even if
|
||||
* the clipboard API is unavailable. */
|
||||
* the clipboard API is unavailable.
|
||||
*
|
||||
* The open link ships hidden: followed in the browser the rewrite rules are
|
||||
* installed in, it redirects straight back here, so it is only worth showing
|
||||
* once a browser has been picked to hand it to. The script reveals it, and
|
||||
* the href in the markup is the plain address it starts from. */
|
||||
export function originalUrlBlock(originalUrl: string, platformLabel: string): Raw {
|
||||
return html`<div class="original">
|
||||
<button type="button" class="copy" data-url="${originalUrl}">
|
||||
<span class="copy__idle">Copy original link</span>
|
||||
<span class="copy__done" hidden>Copied</span>
|
||||
</button>
|
||||
<a class="original__open" href="${originalUrl}" rel="noopener noreferrer nofollow" target="_blank">Open on ${platformLabel}</a>
|
||||
<a class="original__open" href="${originalUrl}" rel="noopener noreferrer nofollow" target="_blank" hidden>Open on ${platformLabel}</a>
|
||||
<p class="original__url"><code>${originalUrl}</code></p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
+5
-1
@@ -105,8 +105,12 @@ test('a failure still hands the link back', () => {
|
||||
detail: 'TikTok showed a verification puzzle instead of the post.',
|
||||
});
|
||||
assert.ok(page.includes('data-url="https://www.tiktok.com/@a/video/1"'));
|
||||
assert.ok(page.includes('Open on TikTok'));
|
||||
assert.ok(page.includes('<code>https://www.tiktok.com/@a/video/1</code>'));
|
||||
assert.ok(page.includes('verification puzzle'));
|
||||
// Following the original link in the browser the rewrite rules are
|
||||
// installed in only comes back here, so it stays hidden until the script
|
||||
// has a browser to hand it to.
|
||||
assert.ok(/<a class="original__open"[^>]* hidden>Open on TikTok<\/a>/.test(page));
|
||||
});
|
||||
|
||||
function redditPost(overrides: Partial<Post> = {}): Post {
|
||||
|
||||
Reference in New Issue
Block a user