The plain address is the one thing that button must not offer -- followed in the browser the rewrite rules are installed in, it comes straight back here -- so there is nothing to show until there is a browser to hand it to. The copy button and the selectable URL were always the part carrying the weight; the open link now ships hidden and the script reveals it along with the scheme. That also settles what the button should say. It went back to "Open on <platform>": the browser's name was there to explain a tap that went nowhere, and a button that is not shown until it works needs no such explanation. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KF5YF3iZVKbezwALap8LYd
This commit is contained in:
@@ -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
|
`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.
|
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
|
Until a browser is chosen there is no "Open on …" button at all — the plain address is
|
||||||
by the script afterwards; with JavaScript off, or with the default left alone, nothing
|
the one thing it must not offer, since following it only comes back here. The markup
|
||||||
changes. Once a browser is chosen the button says which one, because a scheme for a
|
carries that address anyway, hidden, and the script swaps in the scheme and reveals the
|
||||||
browser that isn't installed opens nothing at all, and a silent tap needs an
|
button once there is somewhere to send it. So with JavaScript off, or with the default
|
||||||
explanation. The URL underneath stays selectable either way.
|
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
|
## How it works
|
||||||
|
|
||||||
|
|||||||
+8
-11
@@ -1,7 +1,7 @@
|
|||||||
// Progressive enhancement only. Without this file the page still shows the
|
// Progressive enhancement only. Without this file the page still shows the
|
||||||
// media in a swipeable rail and the original URL as selectable text.
|
// 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 VIEW_KEY = 'antisocial:view';
|
||||||
const BROWSER_KEY = 'antisocial:browser';
|
const BROWSER_KEY = 'antisocial:browser';
|
||||||
@@ -48,24 +48,21 @@ function storedBrowser() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The markup carries the plain address, so the link still goes somewhere
|
// The markup carries the plain address, which is the one thing the button
|
||||||
// with this file missing. Only once a browser has been chosen is the href
|
// must not offer: followed in the browser the rewrite rules are installed
|
||||||
// swapped for that browser's scheme -- and the name goes onto the button,
|
// in, it redirects straight back to this page. So the link ships hidden and
|
||||||
// because a scheme for a browser that is not installed opens nothing and
|
// is only revealed once a browser has been picked to hand it to. With no
|
||||||
// the tap would otherwise be a silent no-op.
|
// 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) {
|
function applyBrowser(id) {
|
||||||
const os = detectOs();
|
const os = detectOs();
|
||||||
const label = browserById(id)?.label;
|
|
||||||
|
|
||||||
for (const link of document.querySelectorAll('.original__open')) {
|
for (const link of document.querySelectorAll('.original__open')) {
|
||||||
link.dataset.original ??= link.getAttribute('href');
|
link.dataset.original ??= link.getAttribute('href');
|
||||||
link.dataset.label ??= link.textContent.trim();
|
|
||||||
|
|
||||||
const href = openUrlFor(link.dataset.original, id, os);
|
const href = openUrlFor(link.dataset.original, id, os);
|
||||||
link.href = href;
|
link.href = href;
|
||||||
link.textContent = href === link.dataset.original
|
link.hidden = href === link.dataset.original;
|
||||||
? link.dataset.label
|
|
||||||
: `${link.dataset.label} in ${label}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ export function browsersFor(os) {
|
|||||||
return BROWSERS.filter((browser) => browser.id === 'default' || browser[os]);
|
return BROWSERS.filter((browser) => browser.id === 'default' || browser[os]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function browserById(id) {
|
function browserById(id) {
|
||||||
return BROWSERS.find((browser) => browser.id === 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">
|
<footer class="post__foot">
|
||||||
<div class="original">
|
<div class="original">
|
||||||
<button type="button" class="copy" id="give-up">Give up and show me the link</button>
|
<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}
|
Open on ${challenge.platformLabel}
|
||||||
</a>
|
</a>
|
||||||
<p class="original__url"><code>${challenge.originalUrl}</code></p>
|
<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 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 {
|
export function originalUrlBlock(originalUrl: string, platformLabel: string): Raw {
|
||||||
return html`<div class="original">
|
return html`<div class="original">
|
||||||
<button type="button" class="copy" data-url="${originalUrl}">
|
<button type="button" class="copy" data-url="${originalUrl}">
|
||||||
<span class="copy__idle">Copy original link</span>
|
<span class="copy__idle">Copy original link</span>
|
||||||
<span class="copy__done" hidden>Copied</span>
|
<span class="copy__done" hidden>Copied</span>
|
||||||
</button>
|
</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>
|
<p class="original__url"><code>${originalUrl}</code></p>
|
||||||
</div>`;
|
</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.',
|
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('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'));
|
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 {
|
function redditPost(overrides: Partial<Post> = {}): Post {
|
||||||
|
|||||||
Reference in New Issue
Block a user