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

Copy/Paste Option for adding Book Covers resolving #9993 #10101

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
10 changes: 9 additions & 1 deletion openlibrary/i18n/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4320,13 +4320,21 @@ msgid "Upload"
msgstr ""

#: covers/add.html
msgid "Or, use the the cover from Internet Archive"
msgid "Or, paste an image from your clipboard"
msgstr ""

#: covers/add.html
msgid "No Image Pasted"
msgstr ""

#: covers/add.html
msgid "Uploading..."
msgstr ""

#: covers/add.html
msgid "Or, use the the cover from Internet Archive"
msgstr ""

#: covers/author_photo.html
msgid "Pull up a larger author photo"
msgstr ""
Expand Down
73 changes: 73 additions & 0 deletions openlibrary/plugins/openlibrary/js/covers.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,76 @@ export function initCoversSaved() {
parent.$(cover_selector).attr('src', cover_url);
}
}

// This function will be triggered when the user clicks the "Paste" button
export async function pasteImage() {
let formData = null;
try {
const clipboardItems = await navigator.clipboard.read();
for (const item of clipboardItems) {
if (item.types.includes('image/png')) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah there's a bug here; since there can be multiple items in the clipboardItems, you'll want to skip any non image types, not error. Eg

if (!item.types.includes('image/png')) {
    continue;
}

And also check for jpg images as well, since those will work too!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes done. Additionally, initially I had included only png support because clipboards store all copied image formats in .png irrespective of the initial image format. However after a little digging after your suggestion, I found out that in Windows, users can changes their settings to store images in the jpg format. I may have to dig deeper into this, but I do not see any harm in adding the support right now.

const blob = await item.getType('image/png');
const image = document.getElementById('image');
image.src = URL.createObjectURL(blob);
image.style.display = 'block';

// Update the global formData with the new image blob
formData = new FormData();
formData.append('file', blob, 'pasted-image.png');

// Automatically fill in the hidden file input with the FormData
const fileInput = document.getElementById('hiddenFileInput');
const file = new File([blob], 'pasted-image.png', { type: 'image/png' });
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files; // This sets the file input with the image

// Show the upload button and update its text
const uploadButton = document.getElementById('uploadButton2');
uploadButton.style.display = 'inline';
uploadButton.innerText = 'Use this image';

// Update the paste button text
document.getElementById('pasteButton').innerText = 'Change Image';

return formData;
} else {
throw new Error('Clipboard does not contain PNG image data.');
Spaarsh marked this conversation as resolved.
Show resolved Hide resolved
}
}
} catch (error) {

}
}

export function initPasteForm(formData) {
Spaarsh marked this conversation as resolved.
Show resolved Hide resolved
document.getElementById('uploadButton2').addEventListener('click', function(event) {
event.preventDefault(); // Prevent the default form submission

const form = document.getElementById('clipboardForm');
if (formData) {
// Show the loading indicator
const loadingIndicator = document.querySelector('.loadingIndicator');
const formDivs = document.querySelectorAll('.ol-cover-form, .imageIntro');

if (loadingIndicator) {
loadingIndicator.classList.remove('hidden');
formDivs.forEach(div => div.classList.add('hidden'));
}

// Submit the form
form.submit();

// Hide the loading indicator after a delay to simulate form submission time
setTimeout(() => {
if (loadingIndicator) {
loadingIndicator.classList.add('hidden');
formDivs.forEach(div => div.classList.remove('hidden'));
}
initCoversSaved(); // Trigger the initCoversSaved function
}, 3000); // Delay Added
} else {
alert('No image data to upload.');
}
});
}
13 changes: 13 additions & 0 deletions openlibrary/plugins/openlibrary/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,19 @@ jQuery(function () {
});
}

const coverForm = document.querySelector('.ol-cover-form--clipboard');
Spaarsh marked this conversation as resolved.
Show resolved Hide resolved
if (coverForm) {
import(/* webpackChunkName: "covers" */ './covers')
.then(module => {
if (coverForm) {
document.getElementById('pasteButton').addEventListener('click', async () => {
const formData = await module.pasteImage();
module.initPasteForm(formData);
});
}
});
}

if (document.getElementById('addbook')) {
import(/* webpackChunkName: "add-book" */ './add-book')
.then(module => module.initAddBookImport());
Expand Down
18 changes: 17 additions & 1 deletion openlibrary/templates/covers/add.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,26 @@
</div>
<div class="input">
<input type="file" name="file" id="coverFile" value="" accept=".jpg, .jpeg, .gif, .png" required/>
<button type="submit" class="cta-btn cta-btn--vanilla">$_("Upload")</button>
<button id='uploadButton1' type="submit" class="cta-btn cta-btn--vanilla">$_("Upload")</button>
Spaarsh marked this conversation as resolved.
Show resolved Hide resolved
</div>
</form>

<form id="clipboardForm" class="ol-cover-form ol-cover-form--clipboard" method="post" enctype="multipart/form-data" action="$action">
<div class="label">
<label for="coverClipboard">$_("Or, paste an image from your clipboard")</label>
</div>
<div class="input">
<button type="button" onclick="pasteImage()" class="cta-btn cta-btn--vanilla" id="pasteButton">$_("No Image Pasted")</button>
Spaarsh marked this conversation as resolved.
Show resolved Hide resolved
</div>
<!-- Hidden file input that will be populated with the pasted image -->
<input type="file" name="file" id="hiddenFileInput" style="display: none;" />
<button id="uploadButton2" type="submit" class="cta-btn cta-btn--vanilla" style="display: none;">$_("Upload")</button>
</form>

<div id="imageContainer" style="text-align: center; margin-top: 20px;">
<img id="image" style="max-width: 100%; max-height: 400px; display: none; margin: 0 auto;" />
</div>
$:macros.LoadingIndicator(_("Uploading..."))
$#<form class="ol-cover-form ol-cover-form--url" method="post" enctype="multipart/form-data" action="$action">
$# <div class="label">
$# <label id="imageWeb" for="imageUrl">$_("Or, paste in the image URL if it's on the web")</label>
Expand Down
Loading