1
0
mirror of https://github.com/koloml/furbooru-tagging-assistant.git synced 2025-12-23 23:02:58 +00:00

Merge pull request #106 from koloml/feature/workaronud-for-opening-in-new-tab

Fixed popup links being unusable when opened in new tab
This commit is contained in:
2025-02-28 03:57:38 +04:00
committed by GitHub
4 changed files with 139 additions and 3 deletions

View File

@@ -1,8 +1,13 @@
/** @type {import('@sveltejs/kit').Reroute} */
export function reroute({url}) {
import type { Reroute } from "@sveltejs/kit";
export const reroute: Reroute = ({url}) => {
// Reroute index.html as just / for the root.
// Browser extension starts from with the index.html file in the pathname which is not correct for the router.
if (url.pathname === '/index.html') {
if (url.searchParams.has('path')) {
return url.searchParams.get('path')!;
}
return "/";
}
}
};

48
src/lib/popup-links.ts Normal file
View File

@@ -0,0 +1,48 @@
function resolveReplaceableLink(target: EventTarget | null = null): HTMLAnchorElement | null {
if (!(target instanceof HTMLElement)) {
return null;
}
const closestLink = target.closest('a');
if (
closestLink instanceof HTMLAnchorElement
&& !closestLink.search
&& closestLink.origin === location.origin
) {
return closestLink;
}
return null;
}
function replaceLink(linkElement: HTMLAnchorElement) {
const params = new URLSearchParams([
['path', linkElement.pathname]
]);
linkElement.search = params.toString();
linkElement.pathname = "/index.html";
}
export function initializeLinksReplacement(): () => void {
const abortController = new AbortController();
const replacementHandler = (event: Event) => {
const closestLink = resolveReplaceableLink(event.target);
if (closestLink) {
replaceLink(closestLink);
}
}
// Dynamically replace the links from the Svelte default links to the links usable for the popup.
document.body.addEventListener('mousedown', replacementHandler, {
signal: abortController.signal,
});
document.body.addEventListener('click', replacementHandler, {
signal: abortController.signal,
})
return () => abortController.abort();
}

View File

@@ -2,6 +2,8 @@
import "../styles/popup.scss";
import Header from "$components/layout/Header.svelte";
import Footer from "$components/layout/Footer.svelte";
import { initializeLinksReplacement } from "$lib/popup-links";
import { onDestroy } from "svelte";
interface Props {
children?: import('svelte').Snippet;
@@ -12,6 +14,12 @@
// Sort of a hack, detect if we rendered in the browser tab or in the popup.
// Popup will always should have fixed 320px size, otherwise we consider it opened in the tab.
document.body.classList.toggle('is-in-tab', window.innerWidth > 320);
const disconnectLinkReplacement = initializeLinksReplacement();
onDestroy(() => {
disconnectLinkReplacement();
})
</script>
<Header/>

View File

@@ -0,0 +1,75 @@
import { randomString } from "$tests/utils";
import { initializeLinksReplacement } from "$lib/popup-links";
describe('popup-links', () => {
let expectedPath = '';
let testLink: HTMLAnchorElement = document.createElement('a');
let disconnectCallback: (() => void) | null = null;
function fireEventAt(target: EventTarget, eventName: string) {
target.dispatchEvent(new Event(eventName, {bubbles: true}));
}
beforeEach(() => {
expectedPath = `/test/${randomString()}`;
testLink.href = expectedPath;
document.body.append(testLink);
});
afterEach(() => {
if (disconnectCallback) {
disconnectCallback();
disconnectCallback = null;
}
});
it('should replace link on any mouse button down', () => {
disconnectCallback = initializeLinksReplacement();
fireEventAt(testLink, "mousedown");
const resultUrl = new URL(testLink.href);
expect(resultUrl.searchParams.get('path')).toBe(expectedPath);
});
it('should replace link when link is pressed by keyboard or clicked', () => {
disconnectCallback = initializeLinksReplacement();
fireEventAt(testLink, "click");
const resultUrl = new URL(testLink.href);
expect(resultUrl.searchParams.get('path')).toBe(expectedPath);
});
it('should not replace already replaced links', () => {
disconnectCallback = initializeLinksReplacement();
fireEventAt(testLink, "click");
const hrefAfterFirstClick = testLink.href;
fireEventAt(testLink, "click");
const hrefAfterSecondClick = testLink.href;
expect(hrefAfterFirstClick).toBe(hrefAfterSecondClick);
});
it('should stop replacing links once disconnect is called', () => {
const hrefBefore = testLink.href;
disconnectCallback = initializeLinksReplacement();
disconnectCallback();
fireEventAt(testLink, "mousedown");
fireEventAt(testLink, "click");
expect(hrefBefore).toBe(testLink.href);
});
it('should not touch links with different origin', () => {
testLink.href = "https://external.example.com/" + randomString() + "/";
const hrefBefore = testLink.href;
disconnectCallback = initializeLinksReplacement();
fireEventAt(testLink, "click");
expect(testLink.href).toBe(hrefBefore);
});
});