Skip to content

Commit facc4e1

Browse files
raphaelweisnitram35
andcommitted
FEATURE: Upload a picture into a document (see #153).
Co-Authored-By: Martin Gandon <[email protected]>
1 parent d62b999 commit facc4e1

7 files changed

Lines changed: 62 additions & 39 deletions

File tree

frontend/src/components/EditableText.js

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import '../styles/EditableText.css';
22

3-
import { useState, useEffect } from 'react';
3+
import { useState } from 'react';
44
import FormattedText from './FormattedText';
5+
import { v4 as uuid } from 'uuid';
6+
import React from 'react';
57
import PassageMarginMenu from './PassageMarginMenu';
6-
import {v4 as uuid} from 'uuid';
78

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

15-
let parsePassage = (rawText) => (rubric)
16-
? rawText.match(PASSAGE)[1]
17-
: rawText;
16+
let parsePassage = (rawText) => rubric ? rawText.match(PASSAGE)[1] : rawText;
1817

1918
let parseFirstPassage = (rawText) => {
2019
let parsed = rawText.match(FIRST_PASSAGE);
@@ -47,12 +46,20 @@ function EditableText({id, text, rubric, isPartOf, links, fragment, setFragment,
4746
updateEditedDocument()
4847
.then((x) => {
4948
setEditedText(parsePassage(x.text));
49+
})
50+
.catch(() => {
51+
setEditedDocument({
52+
_id: uuid(),
53+
text: `{${rubric}} ${text}`,
54+
isPartOf,
55+
links
56+
});
5057
});
5158
};
5259

5360
let handleImageUrl = (imageTag) => {
5461
backend.getDocument(id).then((editedDocument) => {
55-
let parsedText = parsePassage(editedDocument.text) + imageTag;
62+
let parsedText = parseFirstPassage(editedText) + imageTag;
5663
let text = (rubric)
5764
? editedDocument.text.replace(PASSAGE, `{${rubric}} ${parsedText}`)
5865
: parsedText;
@@ -88,17 +95,17 @@ function EditableText({id, text, rubric, isPartOf, links, fragment, setFragment,
8895
if (!beingEdited) return (
8996
<div className="editable content" title="Edit content...">
9097
<div className="formatted-text" onClick={handleClick}>
91-
<FormattedText {...{setHighlightedText}}>
98+
<FormattedText>
9299
{editedText || text}
93100
</FormattedText>
94101
</div>
95-
<PassageMarginMenu {... {id, backend, handleImageUrl}}/>
102+
<PassageMarginMenu id={id} backend={backend} handleImageUrl={handleImageUrl}/>
96103
</div>
97104
);
98105
return (
99106
<form>
100-
<textarea className="form-control" type="text" rows="5" autoFocus
101-
value={editedText} onChange={handleChange} onBlur={handleBlur}
107+
<textarea className="form-control" rows="5" autoFocus value={editedText} onChange={handleChange}
108+
onBlur={handleBlur}
102109
/>
103110
</form>
104111
);

frontend/src/components/Passage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function Rubric({id}) {
5858
);
5959
}
6060

61-
function PassageMargin({active, scholia, setHighlightedText, fragment, setFragment, backend, setLastUpdate}) {
61+
function PassageMargin({active, scholia, backend, setLastUpdate}) {
6262
if (!active) return;
6363
return (
6464
<Col xs={5} className="scholium">

frontend/src/components/PassageMarginMenu.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import '../styles/PassageMarginMenu.css';
2-
31
import { Dropdown } from 'react-bootstrap';
4-
import {forwardRef, useRef} from 'react';
2+
import React, { useRef } from 'react';
53
import { ThreeDotsVertical } from 'react-bootstrap-icons';
64

75
function PassageMarginMenu ({ id, backend, handleImageUrl }) {
@@ -14,7 +12,7 @@ function PassageMarginMenu ({ id, backend, handleImageUrl }) {
1412
const handleFileChange = (event) => {
1513
const file = event.target.files[0];
1614
if (file) backend.putAttachment(id, file, (response) => {
17-
handleImageUrl(`![<IMAGE DESCRIPTION>](${response.url})`);
15+
handleImageUrl(`![${file.name}](${response.url})`);
1816
});
1917
};
2018

@@ -37,7 +35,7 @@ function PassageMarginMenu ({ id, backend, handleImageUrl }) {
3735
);
3836
}
3937

40-
const BlockMenuButton = forwardRef(({ children, onClick }, ref) => (
38+
const BlockMenuButton = React.forwardRef(({ children, onClick }, ref) => (
4139
<ThreeDotsVertical
4240
onClick={(e) => {
4341
e.preventDefault();

frontend/src/hyperglosae.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import {Buffer} from 'buffer';
1+
import { Buffer } from 'buffer';
22

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

5-
function Hyperglosae(logger) {
5+
function Hyperglosae (logger) {
66

77
this.credentials = {};
88

9-
this.getView = ({view, id, options = []}) =>
9+
this.getView = ({ view, id, options = [] }) =>
1010
fetch(`${
1111
service
1212
}/_design/app/_view/${
@@ -23,8 +23,8 @@ function Hyperglosae(logger) {
2323
fetch(`${service}/${id}`)
2424
.then(x => x.json());
2525

26-
let basicAuthentication = ({force}) => {
27-
let {name, password} = this.credentials;
26+
let basicAuthentication = ({ force }) => {
27+
let { name, password } = this.credentials;
2828
if (!force && !name && !password) return ({});
2929
return ({
3030
'Authorization': 'Basic ' + Buffer.from(`${name}:${password}`).toString('base64')
@@ -34,7 +34,7 @@ function Hyperglosae(logger) {
3434
this.putDocument = (doc) =>
3535
fetch(`${service}/${doc._id}`, {
3636
method: 'PUT',
37-
headers: basicAuthentication({force: false}),
37+
headers: basicAuthentication({ force: false }),
3838
body: JSON.stringify(doc)
3939
})
4040
.then(x => x.json())
@@ -46,13 +46,14 @@ function Hyperglosae(logger) {
4646
return x;
4747
});
4848

49-
this.getDocumentMetadata = (id) =>
50-
fetch(`${service}/${id}`, {
49+
this.getDocumentMetadata = (id) => {
50+
return fetch(`${service}/${id}`, {
5151
method: 'HEAD',
5252
headers: basicAuthentication({ force: false })
5353
});
54+
};
5455

55-
this.putAttachment = (id, attachment, callback) =>
56+
this.putAttachment = (id, attachment, callback) => {
5657
this.getDocumentMetadata(id).then(x => {
5758
const reader = new FileReader();
5859
reader.readAsArrayBuffer(attachment);
@@ -71,12 +72,13 @@ function Hyperglosae(logger) {
7172
}).then(response => callback(response));
7273
};
7374
});
75+
};
7476

75-
this.authenticate = ({name, password}) => {
76-
this.credentials = {name, password};
77+
this.authenticate = ({ name, password }) => {
78+
this.credentials = { name, password };
7779
return fetch(`${service}`, {
7880
method: 'GET',
79-
headers: basicAuthentication({force: true})
81+
headers: basicAuthentication({ force: true })
8082
})
8183
.then(x => x.json())
8284
.then(x => {
@@ -89,7 +91,7 @@ function Hyperglosae(logger) {
8991
};
9092

9193
this.refreshMetadata = (id, callback) => {
92-
this.getView({view: 'metadata', id, options: ['include_docs']})
94+
this.getView({ view: 'metadata', id, options: ['include_docs'] })
9395
.then(
9496
(rows) => {
9597
let documents = rows.map(x => x.doc);
@@ -99,7 +101,7 @@ function Hyperglosae(logger) {
99101
};
100102

101103
this.refreshContent = (id, callback) => {
102-
this.getView({view: 'content', id, options: ['include_docs']})
104+
this.getView({ view: 'content', id, options: ['include_docs'] })
103105
.then(
104106
(rows) => {
105107
callback(rows);
@@ -112,11 +114,11 @@ function Hyperglosae(logger) {
112114

113115
this.refreshDocuments = (callback) => {
114116
let id = this.credentials.name || 'PUBLIC';
115-
this.getView({view: 'all_documents', id, options: ['group']})
117+
this.getView({ view: 'all_documents', id, options: ['group'] })
116118
.then((rows) => {
117119
callback(
118120
rows.map(
119-
({value}) => ({...value.metadata, referenced: value.referenced})
121+
({ value }) => ({ ...value.metadata, referenced: value.referenced })
120122
)
121123
);
122124
});

frontend/src/routes/Lectern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function Lectern({backend}) {
7474
if (isPartOf === id) {
7575
part.source.push(text);
7676
} else {
77-
part.scholia = [...part.scholia || [], {id: x.id, rev: x.rev, text, isPartOf, rubric: x.key[1]}];
77+
part.scholia = [...part.scholia || [], {id: x.id, rev: lastUpdate, text, isPartOf, rubric: x.key[1]}];
7878
}
7979
if (i === length - 1) {
8080
return [...whole, part];

frontend/src/styles/EditableText.css

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.content {
1+
.editable {
22
display: flex;
33
}
44

@@ -11,4 +11,21 @@
1111
border-color: black;
1212
}
1313

14+
.editable-button {
15+
visibility: hidden;
16+
margin-top: 0.25rem;
17+
color: crimson;
18+
}
19+
20+
.editable:hover .editable-button {
21+
visibility: visible;
22+
cursor: pointer;
23+
}
24+
25+
#block-actions .dropdown-item:hover {
26+
background-color: lightgray;
27+
}
1428

29+
#block-actions .dropdown-item:active {
30+
color: black;
31+
}

frontend/src/styles/Lectern.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,3 @@ p, ul, h1, h2 {
8080
dd {
8181
padding-left: 30px;
8282
}
83-

0 commit comments

Comments
 (0)