Skip to content

Commit

Permalink
feat(topics): basic image cropping ui works
Browse files Browse the repository at this point in the history
  • Loading branch information
nsantacruz committed Dec 3, 2024
1 parent d84e399 commit 8bbf7fe
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 86 deletions.
83 changes: 2 additions & 81 deletions static/js/Misc.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1440,85 +1440,6 @@ const SmallBlueButton = ({onClick, tabIndex, text}) => {
);
};

const TopicPictureUploader = ({title, slug, callback, old_filename, caption}) => {
/*
`old_filename` is passed to API so that if it exists, it is deleted
*/
const fileInput = useRef(null);

const uploadImage = function(imageData, type="POST") {
const formData = new FormData();
formData.append('file', imageData.replace(/data:image\/(jpe?g|png|gif);base64,/, ""));
if (old_filename !== "") {
formData.append('old_filename', old_filename);
}
const request = new Request(
`${Sefaria.apiHost}/api/topics/images/${slug}`,
{headers: {'X-CSRFToken': Cookies.get('csrftoken')}}
);
fetch(request, {
method: 'POST',
mode: 'same-origin',
credentials: 'same-origin',
body: formData
}).then(response => {
if (!response.ok) {
response.text().then(resp_text=> {
alert(resp_text);
})
}else{
response.json().then(resp_json=>{
callback(resp_json.url);
});
}
}).catch(error => {
alert(error);
})};
const onFileSelect = (e) => {
const file = fileInput.current.files[0];
if (file == null)
return;
if (/\.(jpe?g|png|gif)$/i.test(file.name)) {
const reader = new FileReader();

reader.addEventListener("load", function() {
uploadImage(reader.result);
}, false);

reader.addEventListener("onerror", function() {
alert(reader.error);
}, false);

reader.readAsDataURL(file);
} else {
alert('The file is not an image');
}
}
const deleteImage = () => {
const old_filename_wout_url = old_filename.split("/").slice(-1);
const url = `${Sefaria.apiHost}/api/topics/images/${slug}?old_filename=${old_filename_wout_url}`;
Sefaria.adminEditorApiRequest(url, null, null, "DELETE").then(() => alert("Deleted image."));
callback("");
fileInput.current.value = "";
}
return <div className="section">
<label><InterfaceText>Picture</InterfaceText></label>
<label>
<span className="optional"><InterfaceText>Please use horizontal, square, or only-slightly-vertical images for best results.</InterfaceText></span>
</label>
<div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton">
<label htmlFor="addImageFileSelector">
<SmallBlueButton tabIndex="0" text="Upload Picture" />
</label>
</div><input style={{display: "none"}} id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} />
{old_filename !== "" && <div style={{"max-width": "420px"}}>
<br/><ImageWithCaption photoLink={old_filename} caption={caption}/>
<br/>
<SmallBlueButton tabIndex="1" text="Remove Picture" onClick={deleteImage} />
</div>
}
</div>
}

const CategoryColorLine = ({category}) =>
<div className="categoryColorLine" style={{background: Sefaria.palette.categoryColor(category)}}/>;
Expand Down Expand Up @@ -3320,9 +3241,9 @@ export {
CategoryChooser,
TitleVariants,
OnInView,
TopicPictureUploader,
ImageWithCaption,
handleAnalyticsOnMarkdown,
LangSelectInterface,
PencilSourceEditor
PencilSourceEditor,
SmallBlueButton,
};
97 changes: 92 additions & 5 deletions static/js/TopicEditor.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,101 @@
import Sefaria from "./sefaria/sefaria";
import {InterfaceText, TopicPictureUploader} from "./Misc";
import {InterfaceText, SmallBlueButton, ImageWithCaption} from "./Misc";
import $ from "./sefaria/sefariaJquery";
import {AdminEditor} from "./AdminEditor";
import {Reorder} from "./CategoryEditor";
import {ImageCropper} from "./ImageCropper";
import React, {useState, useEffect} from "react";
import React, {useState, useRef} from "react";


const TopicPictureCropper = ({}) => {
return (<div>hello</div>);
const TopicPictureUploader = ({slug, callback, old_filename, caption}) => {
/*
`old_filename` is passed to API so that if it exists, it is deleted
*/
const fileInput = useRef(null);

const uploadImage = function(imageData, type="POST") {
const formData = new FormData();
formData.append('file', imageData.replace(/data:image\/(jpe?g|png|gif);base64,/, ""));
if (old_filename !== "") {
formData.append('old_filename', old_filename);
}
const request = new Request(
`${Sefaria.apiHost}/api/topics/images/${slug}`,
{headers: {'X-CSRFToken': Cookies.get('csrftoken')}}
);
fetch(request, {
method: 'POST',
mode: 'same-origin',
credentials: 'same-origin',
body: formData
}).then(response => {
if (!response.ok) {
response.text().then(resp_text=> {
alert(resp_text);
})
}else{
response.json().then(resp_json=>{
callback(resp_json.url);
});
}
}).catch(error => {
alert(error);
})};
const onFileSelect = (e) => {
const file = fileInput.current.files[0];
if (file == null)
return;
if (/\.(jpe?g|png|gif)$/i.test(file.name)) {
const reader = new FileReader();

reader.addEventListener("load", function() {
uploadImage(reader.result);
}, false);

reader.addEventListener("onerror", function() {
alert(reader.error);
}, false);

reader.readAsDataURL(file);
} else {
alert('The file is not an image');
}
}
const deleteImage = () => {
const old_filename_wout_url = old_filename.split("/").slice(-1);
const url = `${Sefaria.apiHost}/api/topics/images/${slug}?old_filename=${old_filename_wout_url}`;
Sefaria.adminEditorApiRequest(url, null, null, "DELETE").then(() => alert("Deleted image."));
callback("");
fileInput.current.value = "";
}
return <div className="section">
<label><InterfaceText>Picture</InterfaceText></label>
<label>
<span className="optional"><InterfaceText>Please use horizontal, square, or only-slightly-vertical images for best results.</InterfaceText></span>
</label>
<div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton">
<label htmlFor="addImageFileSelector">
<SmallBlueButton tabIndex="0" text="Upload Picture" />
</label>
</div><input style={{display: "none"}} id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} />
{old_filename !== "" && <div style={{"max-width": "420px"}}>
<br/><ImageWithCaption photoLink={old_filename} caption={caption}/>
<br/>
<SmallBlueButton tabIndex="1" text="Remove Picture" onClick={deleteImage} />
</div>
}
</div>
}


const TopicPictureCropper = ({image_uri}) => {
const [imageToCrop, setImageToCrop] = useState(null);
return (
<div>
<SmallBlueButton tabIndex="0" onClick={() => setImageToCrop(image_uri)} text="Upload Secondary Picture" />
<ImageCropper src={imageToCrop} onClose={() => setImageToCrop(null)}/>
</div>
);
}


Expand Down Expand Up @@ -246,7 +333,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => {
validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} items={items}
pictureUploader={<TopicPictureUploader slug={data.origSlug} callback={handlePictureChange} old_filename={data.image_uri}
caption={{en: data.enImgCaption, he: data.heImgCaption}}/>}
secondaryPictureCropper={<TopicPictureCropper />}
secondaryPictureCropper={<TopicPictureCropper image_uri={data.image_uri}/>}
extras={
[isNew ? null :
<Reorder subcategoriesAndBooks={sortedSubtopics}
Expand Down

0 comments on commit 8bbf7fe

Please sign in to comment.