Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename clipboard functions for clarity and move request and response to own file #334

Merged
merged 1 commit into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions src/background/messaging/handleRequestFromTalon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { retrieve } from "../../common/storage";
import { promiseWrap } from "../../lib/promiseWrap";
import { type RequestFromTalon } from "../../typings/RequestFromTalon";
import { dispatchCommand } from "../commands/dispatchCommand";
import {
getRequestFromClipboard,
writeResponseToClipboard,
} from "../utils/clipboard";
import { getRequest, postResponse } from "../utils/requestAndResponse";
import { constructTalonResponse } from "../utils/constructTalonResponse";
import { notify } from "../utils/notify";
import { shouldTryToFocusDocument } from "../utils/shouldTryToFocusDocument";
Expand All @@ -15,7 +12,7 @@ import { sendRequestToContent } from "./sendRequestToContent";
let talonIsWaitingForResponse = false;

async function writeTypeCharactersResponse() {
await writeResponseToClipboard({
await postResponse({
type: "response",
action: { type: "noHintFound" },
actions: [
Expand Down Expand Up @@ -66,7 +63,7 @@ async function handleDirectClickElementRequest(request: RequestFromTalon) {

export async function handleRequestFromTalon() {
try {
const request = await getRequestFromClipboard();
const request = await getRequest();
if (process.env["NODE_ENV"] !== "production") {
console.log(JSON.stringify(request, null, 2));
}
Expand All @@ -93,13 +90,13 @@ export async function handleRequestFromTalon() {
(await shouldTryToFocusDocument())
) {
const response = constructTalonResponse([{ name: "focusPageAndResend" }]);
await writeResponseToClipboard(response);
await postResponse(response);
return;
}

const response = await dispatchCommand(request.action);
if (talonIsWaitingForResponse) {
await writeResponseToClipboard(response);
await postResponse(response);
talonIsWaitingForResponse = false;
}
} catch (error: unknown) {
Expand Down
149 changes: 57 additions & 92 deletions src/background/utils/clipboard.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import browser from "webextension-polyfill";
import { urls } from "../../common/urls";
import {
type RequestFromTalon,
type ResponseToTalon,
} from "../../typings/RequestFromTalon";
import { notify } from "./notify";
import { isSafari } from "./isSafari";

/**
Expand All @@ -27,7 +22,48 @@ const storageClipboard = {
},
};

async function getClipboardManifestV3(): Promise<string | undefined> {
export async function readClipboard(): Promise<string | undefined> {
if (process.env["NODE_ENV"] === "test") {
return storageClipboard.readText();
}

if (isSafari()) {
return readClipboardSafari();
}

if (navigator.clipboard) {
return navigator.clipboard.readText();
}

return readClipboardManifestV3();
}

export async function writeClipboard(text: string) {
if (process.env["NODE_ENV"] === "test") {
await storageClipboard.writeText(text);
}

if (isSafari()) {
return writeClipboardSafari(text);
}

if (navigator.clipboard) {
return navigator.clipboard.writeText(text);
}

return writeClipboardManifestV3(text);
}

async function readClipboardSafari() {
const response: { textFromClipboard: string } =
await browser.runtime.sendNativeMessage("", {
request: "getTextFromClipboard",
});

return response.textFromClipboard;
}

async function readClipboardManifestV3(): Promise<string | undefined> {
try {
const hasDocument = await chrome.offscreen.hasDocument();
if (!hasDocument) {
Expand All @@ -49,7 +85,21 @@ async function getClipboardManifestV3(): Promise<string | undefined> {
return undefined;
}

async function copyToClipboardManifestV3(text: string) {
async function writeClipboardSafari(text: string) {
const copyPasteArea =
document.querySelector("#copy-paste-area") ??
document.createElement("textarea");
copyPasteArea.id = "copy-paste-area";
document.body.append(copyPasteArea);
if (copyPasteArea instanceof HTMLTextAreaElement) {
copyPasteArea.value = text;
copyPasteArea.select();
document.execCommand("copy");
copyPasteArea.value = "";
}
}

async function writeClipboardManifestV3(text: string) {
try {
const hasDocument = await chrome.offscreen.hasDocument();
if (!hasDocument) {
Expand All @@ -69,88 +119,3 @@ async function copyToClipboardManifestV3(text: string) {
console.error(error);
}
}

async function getTextFromClipboard(): Promise<string | undefined> {
if (process.env["NODE_ENV"] === "test") {
return storageClipboard.readText();
}

if (isSafari()) {
const response: { textFromClipboard: string } =
await browser.runtime.sendNativeMessage("", {
request: "getTextFromClipboard",
});

return response.textFromClipboard;
}

if (navigator.clipboard) {
return navigator.clipboard.readText();
}

return getClipboardManifestV3();
}

export async function getRequestFromClipboard(): Promise<
RequestFromTalon | undefined
> {
const clipText = await getTextFromClipboard();
let request: RequestFromTalon;
if (clipText) {
try {
request = JSON.parse(clipText) as RequestFromTalon;
// This is just to be extra safe
if (request.type !== "request") {
console.error(
'Error: The message present in the clipboard is not of type "request"'
);
}

return request;
} catch (error: unknown) {
// We already check that we are sending valid json in rango-talon, but
// just to be extra sure
if (error instanceof SyntaxError) {
console.error(error);
}
}
} else {
await notify("Unable to read the request present on the clipboard", {
type: "error",
});
}

return undefined;
}

export async function writeResponseToClipboard(response: ResponseToTalon) {
// We send the response so that talon can make sure the request was received
// and to tell talon to execute any actions

const jsonResponse = JSON.stringify(response);

if (process.env["NODE_ENV"] === "test") {
await storageClipboard.writeText(jsonResponse);
}

if (navigator.clipboard) {
if (isSafari()) {
const copyPasteArea =
document.querySelector("#copy-paste-area") ??
document.createElement("textarea");
copyPasteArea.id = "copy-paste-area";
document.body.append(copyPasteArea);
if (copyPasteArea instanceof HTMLTextAreaElement) {
copyPasteArea.value = jsonResponse;
copyPasteArea.select();
document.execCommand("copy");
copyPasteArea.value = "";
return;
}
} else {
return navigator.clipboard.writeText(jsonResponse);
}
}

return copyToClipboardManifestV3(jsonResponse);
}
48 changes: 48 additions & 0 deletions src/background/utils/requestAndResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type {
RequestFromTalon,
ResponseToTalon,
} from "../../typings/RequestFromTalon";
import { readClipboard, writeClipboard } from "./clipboard";
import { notify } from "./notify";

/**
* Reads and parses the request from the clipboard.
*/
export async function getRequest(): Promise<RequestFromTalon | undefined> {
const clipText = await readClipboard();
let request: RequestFromTalon;

if (clipText) {
try {
request = JSON.parse(clipText) as RequestFromTalon;
// This is just to be extra safe
if (request.type !== "request") {
console.error(
'Error: The message present in the clipboard is not of type "request"'
);
}

return request;
} catch (error: unknown) {
// We already check that we are sending valid json in rango-talon, but
// just to be extra sure
if (error instanceof SyntaxError) {
console.error(error);
}
}
} else {
await notify("Unable to read the request present on the clipboard", {
type: "error",
});
}

return undefined;
}

/**
* Stringifies and writes the response to the clipboard.
*/
export async function postResponse(response: ResponseToTalon) {
const jsonResponse = JSON.stringify(response);
await writeClipboard(jsonResponse);
}