Skip to content

Commit

Permalink
FEATURE: Upload a picture into a document (see #153).
Browse files Browse the repository at this point in the history
Co-Authored-By: Martin Gandon <[email protected]>
  • Loading branch information
raphaelweis and nitram35 committed Jun 19, 2024
1 parent d62b999 commit facc4e1
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 39 deletions.
35 changes: 21 additions & 14 deletions frontend/src/components/EditableText.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import '../styles/EditableText.css';

import { useState, useEffect } from 'react';
import { useState } from 'react';
import FormattedText from './FormattedText';
import { v4 as uuid } from 'uuid';
import React from 'react';
import PassageMarginMenu from './PassageMarginMenu';
import {v4 as uuid} from 'uuid';

function EditableText({id, text, rubric, isPartOf, links, fragment, setFragment, setHighlightedText, backend, setLastUpdate}) {
function EditableText ({ id, text, rubric, isPartOf, links, backend, setLastUpdate }) {
const [beingEdited, setBeingEdited] = useState(false);
const [editedDocument, setEditedDocument] = useState();
const [editedText, setEditedText] = useState();
const PASSAGE = new RegExp(`\\{${rubric}} ?([^{]*)`);
const FIRST_PASSAGE = new RegExp('\\{[^}]+} ?([^{]*)');
const [editedText, setEditedText] = useState(text);
const PASSAGE = new RegExp(`\\{${rubric}} ?([^{]+)`);
const FIRST_PASSAGE = new RegExp('\\{[^}]+} ?([^{]+)');

let parsePassage = (rawText) => (rubric)
? rawText.match(PASSAGE)[1]
: rawText;
let parsePassage = (rawText) => rubric ? rawText.match(PASSAGE)[1] : rawText;

let parseFirstPassage = (rawText) => {
let parsed = rawText.match(FIRST_PASSAGE);
Expand Down Expand Up @@ -47,12 +46,20 @@ function EditableText({id, text, rubric, isPartOf, links, fragment, setFragment,
updateEditedDocument()
.then((x) => {
setEditedText(parsePassage(x.text));
})
.catch(() => {
setEditedDocument({
_id: uuid(),
text: `{${rubric}} ${text}`,
isPartOf,
links
});
});
};

let handleImageUrl = (imageTag) => {
backend.getDocument(id).then((editedDocument) => {
let parsedText = parsePassage(editedDocument.text) + imageTag;
let parsedText = parseFirstPassage(editedText) + imageTag;
let text = (rubric)
? editedDocument.text.replace(PASSAGE, `{${rubric}} ${parsedText}`)
: parsedText;
Expand Down Expand Up @@ -88,17 +95,17 @@ function EditableText({id, text, rubric, isPartOf, links, fragment, setFragment,
if (!beingEdited) return (
<div className="editable content" title="Edit content...">
<div className="formatted-text" onClick={handleClick}>
<FormattedText {...{setHighlightedText}}>
<FormattedText>
{editedText || text}
</FormattedText>
</div>
<PassageMarginMenu {... {id, backend, handleImageUrl}}/>
<PassageMarginMenu id={id} backend={backend} handleImageUrl={handleImageUrl}/>
</div>
);
return (
<form>
<textarea className="form-control" type="text" rows="5" autoFocus
value={editedText} onChange={handleChange} onBlur={handleBlur}
<textarea className="form-control" rows="5" autoFocus value={editedText} onChange={handleChange}
onBlur={handleBlur}
/>
</form>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Passage.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function Rubric({id}) {
);
}

function PassageMargin({active, scholia, setHighlightedText, fragment, setFragment, backend, setLastUpdate}) {
function PassageMargin({active, scholia, backend, setLastUpdate}) {
if (!active) return;
return (
<Col xs={5} className="scholium">
Expand Down
8 changes: 3 additions & 5 deletions frontend/src/components/PassageMarginMenu.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import '../styles/PassageMarginMenu.css';

import { Dropdown } from 'react-bootstrap';
import {forwardRef, useRef} from 'react';
import React, { useRef } from 'react';
import { ThreeDotsVertical } from 'react-bootstrap-icons';

function PassageMarginMenu ({ id, backend, handleImageUrl }) {
Expand All @@ -14,7 +12,7 @@ function PassageMarginMenu ({ id, backend, handleImageUrl }) {
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) backend.putAttachment(id, file, (response) => {
handleImageUrl(`![<IMAGE DESCRIPTION>](${response.url})`);
handleImageUrl(`![${file.name}](${response.url})`);
});
};

Expand All @@ -37,7 +35,7 @@ function PassageMarginMenu ({ id, backend, handleImageUrl }) {
);
}

const BlockMenuButton = forwardRef(({ children, onClick }, ref) => (
const BlockMenuButton = React.forwardRef(({ children, onClick }, ref) => (
<ThreeDotsVertical
onClick={(e) => {
e.preventDefault();
Expand Down
34 changes: 18 additions & 16 deletions frontend/src/hyperglosae.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {Buffer} from 'buffer';
import { Buffer } from 'buffer';

const service = 'http://localhost:5984/hyperglosae';

function Hyperglosae(logger) {
function Hyperglosae (logger) {

this.credentials = {};

this.getView = ({view, id, options = []}) =>
this.getView = ({ view, id, options = [] }) =>
fetch(`${
service
}/_design/app/_view/${
Expand All @@ -23,8 +23,8 @@ function Hyperglosae(logger) {
fetch(`${service}/${id}`)
.then(x => x.json());

let basicAuthentication = ({force}) => {
let {name, password} = this.credentials;
let basicAuthentication = ({ force }) => {
let { name, password } = this.credentials;
if (!force && !name && !password) return ({});
return ({
'Authorization': 'Basic ' + Buffer.from(`${name}:${password}`).toString('base64')
Expand All @@ -34,7 +34,7 @@ function Hyperglosae(logger) {
this.putDocument = (doc) =>
fetch(`${service}/${doc._id}`, {
method: 'PUT',
headers: basicAuthentication({force: false}),
headers: basicAuthentication({ force: false }),
body: JSON.stringify(doc)
})
.then(x => x.json())
Expand All @@ -46,13 +46,14 @@ function Hyperglosae(logger) {
return x;
});

this.getDocumentMetadata = (id) =>
fetch(`${service}/${id}`, {
this.getDocumentMetadata = (id) => {
return fetch(`${service}/${id}`, {
method: 'HEAD',
headers: basicAuthentication({ force: false })
});
};

this.putAttachment = (id, attachment, callback) =>
this.putAttachment = (id, attachment, callback) => {
this.getDocumentMetadata(id).then(x => {
const reader = new FileReader();
reader.readAsArrayBuffer(attachment);
Expand All @@ -71,12 +72,13 @@ function Hyperglosae(logger) {
}).then(response => callback(response));
};
});
};

this.authenticate = ({name, password}) => {
this.credentials = {name, password};
this.authenticate = ({ name, password }) => {
this.credentials = { name, password };
return fetch(`${service}`, {
method: 'GET',
headers: basicAuthentication({force: true})
headers: basicAuthentication({ force: true })
})
.then(x => x.json())
.then(x => {
Expand All @@ -89,7 +91,7 @@ function Hyperglosae(logger) {
};

this.refreshMetadata = (id, callback) => {
this.getView({view: 'metadata', id, options: ['include_docs']})
this.getView({ view: 'metadata', id, options: ['include_docs'] })
.then(
(rows) => {
let documents = rows.map(x => x.doc);
Expand All @@ -99,7 +101,7 @@ function Hyperglosae(logger) {
};

this.refreshContent = (id, callback) => {
this.getView({view: 'content', id, options: ['include_docs']})
this.getView({ view: 'content', id, options: ['include_docs'] })
.then(
(rows) => {
callback(rows);
Expand All @@ -112,11 +114,11 @@ function Hyperglosae(logger) {

this.refreshDocuments = (callback) => {
let id = this.credentials.name || 'PUBLIC';
this.getView({view: 'all_documents', id, options: ['group']})
this.getView({ view: 'all_documents', id, options: ['group'] })
.then((rows) => {
callback(
rows.map(
({value}) => ({...value.metadata, referenced: value.referenced})
({ value }) => ({ ...value.metadata, referenced: value.referenced })
)
);
});
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/Lectern.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function Lectern({backend}) {
if (isPartOf === id) {
part.source.push(text);
} else {
part.scholia = [...part.scholia || [], {id: x.id, rev: x.rev, text, isPartOf, rubric: x.key[1]}];
part.scholia = [...part.scholia || [], {id: x.id, rev: lastUpdate, text, isPartOf, rubric: x.key[1]}];
}
if (i === length - 1) {
return [...whole, part];
Expand Down
19 changes: 18 additions & 1 deletion frontend/src/styles/EditableText.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.content {
.editable {
display: flex;
}

Expand All @@ -11,4 +11,21 @@
border-color: black;
}

.editable-button {
visibility: hidden;
margin-top: 0.25rem;
color: crimson;
}

.editable:hover .editable-button {
visibility: visible;
cursor: pointer;
}

#block-actions .dropdown-item:hover {
background-color: lightgray;
}

#block-actions .dropdown-item:active {
color: black;
}
1 change: 0 additions & 1 deletion frontend/src/styles/Lectern.css
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,3 @@ p, ul, h1, h2 {
dd {
padding-left: 30px;
}

0 comments on commit facc4e1

Please sign in to comment.