Skip to content

Commit

Permalink
Copying items is now smooth and way more flexible, new release incoming
Browse files Browse the repository at this point in the history
  • Loading branch information
temporary-simon committed Jul 24, 2023
1 parent 533f830 commit 3af347e
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 71 deletions.
2 changes: 1 addition & 1 deletion src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function buildFormDataFromImage(image) {
}

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.message === "CTRL_V_PRESSED") {
if (request.message === "PASTE_INVOKED") {
const csrfToken = request.csrfToken;
const url = `https://www.overleaf.com/${request.url.substring(
1
Expand Down
51 changes: 24 additions & 27 deletions src/content-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,38 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {
getFolderId,
getClipboardItems,
getImagesAsDTO,
} from "./imageProcessor";

function isPasteEvent(e) {
return (e.ctrlKey || e.metaKey) && e.key === "v";
}
import { getFolderId, getImagesFromClipboardItems } from "./imageProcessor";

const url = document.location.pathname + "/upload";

let folder_id;

window.onload = async function () {
folder_id = await getFolderId();
};

document.addEventListener("keydown", function (e) {
if (!isPasteEvent(e) || !folder_id) return;

(async function () {
const clipboardItems = await getClipboardItems();
const images = await getImagesAsDTO(clipboardItems, folder_id);

if (images) {
var url = document.location.pathname + "/upload";
var csrfToken = document.querySelector(
'meta[name="ol-csrfToken"]'
).content;
chrome.runtime.sendMessage({
message: "CTRL_V_PRESSED",
url: url,
csrfToken: csrfToken,
folder_id: folder_id,
images: images,
});
document.addEventListener("paste", function (e) {
if (!folder_id) return;

(async () => {
let clipboardData = e.clipboardData || window?.clipboardData;
let items = clipboardData.items;
let images = await getImagesFromClipboardItems(items, folder_id);

if (images.length === 0) {
alert("No files were successfully processed.");
return;
}

var csrfToken = document.querySelector('meta[name="ol-csrfToken"]').content;

chrome.runtime.sendMessage({
message: "PASTE_INVOKED",
url: url,
csrfToken: csrfToken,
folder_id: folder_id,
images: images,
});
})();
});
72 changes: 38 additions & 34 deletions src/imageProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { blobToBase64, extensionFromBlob } from "./utils";
import { blobToBase64, getTypeStrFromFile } from "./utils";

/**
* Array of folder names, arranged by priority. This array is used in getImageFolderId function
Expand Down Expand Up @@ -43,39 +43,6 @@ export async function getFolderId() {
}
}

/**
* Reads items from the clipboard.
* @returns {Promise<ClipboardItem[]>} The clipboard's contents as a Promise.
*/
export async function getClipboardItems() {
const clipboardItems = await navigator.clipboard.read();
return clipboardItems;
}

/**
* Formats images from clipboard items for use with Overleaf.
* @param {ClipboardItem[]} clipboardItems - Items from the clipboard.
* @returns {Promise<Object[]>} Promise resolving to an array of image objects formatted for Overleaf.
*/
export async function getImagesAsDTO(clipboardItems, folder_id) {
let imagesPromises = clipboardItems.flatMap((item) => {
return item.types
.filter((type) => type.startsWith("image/"))
.map(async (type) => {
const blob = await item.getType(type);
const base64Data = await blobToBase64(blob);
return {
targetFolderId: folder_id,
name: `rename_me.${extensionFromBlob(blob)}`,
type: blob.type,
qqfile: base64Data,
};
});
});
let images = await Promise.all(imagesPromises);
return images;
}

/**
* This function is used to get the ID of the image folder.
* @returns {Promise} A promise that represents the completion of an asynchronous operation to fetch an image folder ID.
Expand Down Expand Up @@ -110,3 +77,40 @@ function getImageFolderId() {
}, 20000);
});
}

/**
* Processes clipboard items, converting any found images into a specific DTO.
* @param {DataTransferItemList} items - The list of clipboard items to process.
* @param {string} folder_id - The ID of the folder where the images will be stored.
* @returns {Promise<Array>} A promise that resolves to an array of image DTOs.
*/
export async function getImagesFromClipboardItems(items, folder_id) {
const imagePromises = Array.from(items)
.filter((item) => item.kind === "file")
.map((item) => item.getAsFile())
.map(async (blob) => {
const data = await blobToBase64(blob);
const image = createImageDTO(folder_id, blob, data);
return image;
});
return await Promise.all(imagePromises);
}

/**
* Creates an image Data Transfer Object (DTO) using provided parameters.
*
* @param {string} folder_id - The ID of the folder where the image will be stored.
* @param {File} file - The file object containing information about the image.
* @param {string} qqfile - The base64 representation of the image.
*
* @returns {Object} The image DTO with properties: targetFolderId, name, type and qqfile.
*/
export function createImageDTO(folder_id, file, qqfile) {
console.log(file.type);
return {
targetFolderId: folder_id,
name: file.name,
type: getTypeStrFromFile(file),
qqfile: qqfile,
};
}
16 changes: 7 additions & 9 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,19 @@
export function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
resolve(reader.result);
reader.onload = (e) => {
resolve(e.target.result);
};
reader.onerror = reject;
reader.readAsDataURL(blob); //Base64
});
}

/**
* Extracts the extension from a blob's MIME type.
* @param {Blob} blob - The blob to get the extension for.
* @returns {string} The blob's file extension.
* Extracts the file type string from a given file object.
* @param {File} file The file object from which to extract the type.
* @returns {string} The file type as a string if it exists, otherwise an empty string.
*/
export function extensionFromBlob(blob) {
const type = blob.type;
const typeSplitted = type.split("/");
return typeSplitted[typeSplitted.length - 1].trim();
export function getTypeStrFromFile(file) {
return file?.type?.split("/")[1]?.trim() ?? "";
}

0 comments on commit 3af347e

Please sign in to comment.