diff --git a/source/content-scripts/features/group-list-subscribe-button.tsx b/source/content-scripts/features/group-list-subscribe-button.tsx index ee5c9ad..7887b12 100644 --- a/source/content-scripts/features/group-list-subscribe-button.tsx +++ b/source/content-scripts/features/group-list-subscribe-button.tsx @@ -1,5 +1,10 @@ import {Component, render} from "preact"; -import {log, pluralize, querySelectorAll} from "../../utilities/exports.js"; +import { + log, + makeIntercoolerRequest, + pluralize, + querySelectorAll, +} from "../../utilities/exports.js"; export function runGroupListSubscribeButtonFeature(): void { const count = addSubscribeButtonsToGroupList(); @@ -14,14 +19,6 @@ function addSubscribeButtonsToGroupList(): number { return 0; } - const csrfToken = document.querySelector( - 'meta[name="csrftoken"]', - )?.content; - if (csrfToken === undefined) { - log("No CSRF token found", true); - return 0; - } - let count = 0; for (const listItem of querySelectorAll( ".group-list li:not(.trx-group-list-subscribe-button)", @@ -34,14 +31,7 @@ function addSubscribeButtonsToGroupList(): number { } const button = document.createDocumentFragment(); - render( - , - button, - ); + render(, button); const activity = listItem.querySelector(".group-list-activity") ?? undefined; @@ -59,7 +49,6 @@ function addSubscribeButtonsToGroupList(): number { } type Props = { - csrfToken: string; listItem: HTMLLIElement; group: string; }; @@ -80,21 +69,15 @@ class SubscribeButton extends Component { } clickHandler = async () => { - const {csrfToken, group} = this.props; + const {group} = this.props; const {isSubscribed} = this.state; - const response = await window.fetch( + const response = await makeIntercoolerRequest( `https://tildes.net/api/web/group/${group}/subscribe`, { - headers: { - "X-CSRF-Token": csrfToken, - "X-IC-Request": "true", - }, method: isSubscribed ? "DELETE" : "PUT", - referrer: "https://tildes.net", }, ); - if (response.status !== 200) { log(`Unexpected status code: ${response.status}`, true); return; diff --git a/source/utilities/exports.ts b/source/utilities/exports.ts index 3294472..bd997ce 100644 --- a/source/utilities/exports.ts +++ b/source/utilities/exports.ts @@ -3,6 +3,7 @@ export * from "./components/link.js"; export * from "./elements.js"; export * from "./globals.js"; export * from "./groups.js"; +export * from "./http.js"; export * from "./logging.js"; export * from "./query-selectors.js"; export * from "./report-a-bug.js"; diff --git a/source/utilities/http.ts b/source/utilities/http.ts new file mode 100644 index 0000000..fd7f005 --- /dev/null +++ b/source/utilities/http.ts @@ -0,0 +1,50 @@ +import {log} from "./logging.js"; + +/** + * Make an HTTP request to the Tildes API that is normally used by Intercooler. + * This should only be used when using HTML elements from Tildes itself isn't + * feasible. + * @param url The API URL to call. + * @param request Any extra request details, note that some of these values will + * be overridden by the ones required to make a proper Intercooler request. + */ +export async function makeIntercoolerRequest( + url: string, + request: RequestInit, +): Promise { + if (!url.startsWith("https://tildes.net")) { + throw new Error(`Can't make Intercooler request to non-Tildes URL: ${url}`); + } + + const csrfToken = document.querySelector( + 'meta[name="csrftoken"]', + )!.content; + + const ic: RequestInit = { + headers: { + "X-CSRF-Token": csrfToken, + "X-IC-Request": "true", + // Include this header so it's clear this isn't a request actually sent by + // Intercooler but by Tildes ReExtended. + "X-TRX-Request": "true", + }, + referrer: "https://tildes.net", + }; + + request.headers = + request.headers === undefined + ? ic.headers + : { + ...request.headers, + // Apply the Intercooler headers last so they can't be overridden. + ...ic.headers, + }; + + request.referrer = request.referrer ?? ic.referrer; + + // Explicitly log the request because content script HTTP calls don't show up + // in the Network DevTools. + log("Making Intercooler request:"); + log(request); + return window.fetch(url, request); +}