Skip to content

Commit

Permalink
Change writeClipboardText/readClipboardText clipboard fallback to…
Browse files Browse the repository at this point in the history
… in-memory (#2076)

Using the local storage as a fallback caused issues when writing
clipboard contents larger than 5MB. Changing the fallback to in-memory
resolves the issue.

---

<!-- Everything below this line will be removed from the commit message
when the PR is merged -->

## PR Checklist

- [x] Verify if the change requires a changeset. See
[CONTRIBUTING.md](https://github.com/vivid-planet/comet/blob/HEAD/CONTRIBUTING.md)
-   [x] Link to the respective task if one exists: COM-744
  • Loading branch information
johnnyomair committed May 18, 2024
1 parent 9732556 commit 8e3dec5
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 15 deletions.
8 changes: 8 additions & 0 deletions .changeset/plenty-pillows-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@comet/admin": minor
---

Change `writeClipboardText`/`readClipboardText` clipboard fallback to in-memory

Using the local storage as a fallback caused issues when writing clipboard contents larger than 5MB.
Changing the fallback to in-memory resolves the issue.
10 changes: 6 additions & 4 deletions packages/admin/admin/src/clipboard/readClipboardText.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { fallbackClipboardData } from "./writeClipboardText";

export async function readClipboardText(): Promise<string | undefined> {
if (
!("clipboard" in navigator) || // Browser doesn't support navigator.clipboard
navigator.clipboard.readText === undefined // Firefox doesn't support navigator.clipboard.readText() by default
) {
// Reading from clipboard isn't supported, fallback to local storage.
return window.localStorage.getItem("comet_clipboard") ?? undefined;
// Reading from clipboard isn't supported, fallback to in-memory clipboard.
return fallbackClipboardData;
}

try {
Expand All @@ -13,7 +15,7 @@ export async function readClipboardText(): Promise<string | undefined> {
const data = await navigator.clipboard.readText();
return data;
} catch {
console.warn("Clipboard access denied, fallback to local storage.");
return window.localStorage.getItem("comet_clipboard") ?? undefined;
console.warn("Clipboard access denied, fallback to in-memory clipboard.");
return fallbackClipboardData;
}
}
15 changes: 4 additions & 11 deletions packages/admin/admin/src/clipboard/writeClipboardText.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
export let fallbackClipboardData: string | undefined;

export async function writeClipboardText(data: string): Promise<void> {
// Always write to local storage, which is used as a fallback when reading from the clipboard is not supported/allowed.
try {
window.localStorage.setItem("comet_clipboard", data);
} catch (error) {
if (error instanceof DOMException && error.name === "QuotaExceededError") {
// Ignore error when data size exceeds the local storage limit.
// TODO fix by splitting the data into smaller chunks and storing them separately.
} else {
throw error;
}
}
// Always set fallback, which is used when reading from the clipboard is not supported/allowed.
fallbackClipboardData = data;

if (!("clipboard" in navigator)) {
return;
Expand Down
46 changes: 46 additions & 0 deletions storybook/src/admin/clipboard/clipboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Alert, readClipboardText, writeClipboardText } from "@comet/admin";
import { Button, Grid } from "@mui/material";
import { storiesOf } from "@storybook/react";
import React from "react";

storiesOf("@comet/admin/clipboard", module).add("Clipboard fallback size limit", function () {
const writtenClipboardContent = "a".repeat(1024 * 1024 * 10); // 10MB
const [readClipboardContent, setReadClipboardContent] = React.useState<string | undefined>();

return (
<Grid container spacing={2}>
<Grid item xs={12}>
<Alert severity="info">To test this story, either a) disallow clipboard access in your browser, or b) try it in Firefox.</Alert>
</Grid>
<Grid item>
<Button
variant="outlined"
onClick={() => {
writeClipboardText(writtenClipboardContent);
}}
>
Write clipboard
</Button>
</Grid>
<Grid item>
<Button
variant="outlined"
onClick={async () => {
setReadClipboardContent(await readClipboardText());
}}
>
Read clipboard
</Button>
</Grid>
{readClipboardContent && (
<Grid item xs={12}>
{writtenClipboardContent === readClipboardContent ? (
<Alert severity="success">Read clipboard content matches written clipboard content.</Alert>
) : (
<Alert severity="error">Read clipboard content does not match written clipboard content.</Alert>
)}
</Grid>
)}
</Grid>
);
});

0 comments on commit 8e3dec5

Please sign in to comment.