diff --git a/src/editor/Editor.jsx b/src/editor/Editor.jsx index ebc7fe9..b6383bc 100644 --- a/src/editor/Editor.jsx +++ b/src/editor/Editor.jsx @@ -41,8 +41,8 @@ const Editor = props => { // on move. Therefore, don't update if a) props.annotation equals // the currentAnnotation, or props.annotation and currentAnnotations are // a selection, just created by the user. - const preventUpdate = setCurrentAnnotation.isSelection ? - annotation.isSelection : currentAnnotation.annotationId === annotation.annotationId; + const preventUpdate = currentAnnotation?.isSelection ? + annotation?.isSelection : currentAnnotation?.id === annotation.id; if (!preventUpdate) setCurrentAnnotation(annotation); @@ -77,9 +77,8 @@ const Editor = props => { const { user } = props.env; // Metadata is only added when a user is set, otherwise - // the Editor operates in 'anonymous mode'. Also, - // no point in adding meta while we're in draft state - if (!body.draft && user) { + // the Editor operates in 'anonymous mode'. + if (user) { meta.creator = {}; if (user.id) meta.creator.id = user.id; if (user.displayName) meta.creator.name = user.displayName; @@ -116,12 +115,11 @@ const Editor = props => { props.onCancel(currentAnnotation); const onOk = _ => { - // Removes the 'draft' flag from all bodies + // Removes the state payload from all bodies const undraft = annotation => - annotation.clone({ - body : annotation.bodies.map(({ draft, ...rest }) => - draft ? { ...rest, ...creationMeta(rest) } : rest ) - }); + annotation.clone({ + body : annotation.bodies.map(({ draft, ...rest }) => rest) + }); // Current annotation is either a selection (if it was created from // scratch just now) or an annotation (if it existed already and was diff --git a/src/editor/widgets/comment/Comment.jsx b/src/editor/widgets/comment/Comment.jsx index 8560faa..7667307 100644 --- a/src/editor/widgets/comment/Comment.jsx +++ b/src/editor/widgets/comment/Comment.jsx @@ -3,11 +3,10 @@ import { useState } from 'preact/hooks'; import TimeAgo from 'timeago-react'; import DropdownMenu from './DropdownMenu'; import TextEntryField from './TextEntryField'; -import PurposeDropdown from './PurposeDropdown'; +import PurposeSelect from './PurposeSelect'; import { ChevronDownIcon } from '../../../Icons'; import i18n from '../../../i18n'; - /** A single comment inside the CommentWidget **/ const Comment = props => { @@ -15,7 +14,6 @@ const Comment = props => { const [ isMenuVisible, setIsMenuVisible ] = useState(false); const onMakeEditable = _ => { - props.body.draft = true; setIsEditable(true); setIsMenuVisible(false); } @@ -25,13 +23,13 @@ const Comment = props => { setIsMenuVisible(false); } - const onUpdateComment = evt => { + const onUpdateComment = evt => props.onUpdate(props.body, { ...props.body, value: evt.target.value }); - } - const onUpdateDropdown = evt => { + const onChangePurpose = evt => props.onUpdate(props.body, { ...props.body, purpose: evt.value }); - } + + const timestamp = props.body.modified || props.body.created; const creatorInfo = props.body.creator &&
@@ -39,7 +37,7 @@ const Comment = props => { { props.body.created && } @@ -58,13 +56,13 @@ const Comment = props => { onChange={onUpdateComment} onSaveAndClose={props.onSaveAndClose} /> - { creatorInfo } + { !isEditable && creatorInfo } - { props.purpose && - } diff --git a/src/editor/widgets/comment/CommentWidget.jsx b/src/editor/widgets/comment/CommentWidget.jsx index 100221c..1fe0fdc 100644 --- a/src/editor/widgets/comment/CommentWidget.jsx +++ b/src/editor/widgets/comment/CommentWidget.jsx @@ -2,47 +2,47 @@ import React from 'preact/compat'; import Comment from './Comment'; import TextEntryField from './TextEntryField'; import i18n from '../../../i18n'; -import PurposeDropdown, {purposes} from './PurposeDropdown'; +import PurposeSelect, { PURPOSES } from './PurposeSelect'; + +const validPurposes = PURPOSES.map(p => p.value); -const purposeValues = purposes.map(p => p.value); /** * Comments are TextualBodies where the purpose field is either * blank or 'commenting' or 'replying' */ -const isComment = (body, purposenabled) => - body.type === 'TextualBody' && ( - !body.hasOwnProperty('purpose') || purposeCheck(body.purpose, purposenabled) +const isComment = (body, matchAllPurposes) => { + const hasMatchingPurpose = matchAllPurposes ? + validPurposes.indexOf(body.purpose) > -1 : body.purpose == 'commenting' || body.purpose == 'replying'; + + return body.type === 'TextualBody' && ( + !body.hasOwnProperty('purpose') || hasMatchingPurpose ); +} + /** * The draft reply is a comment body with a 'draft' flag */ -const purposeCheck = (purpose, purposenabled) => { - if (purposenabled){ - return purposeValues.indexOf(purpose) > -1 - } else { - return purpose == 'commenting' || purpose == 'replying' - } -} - const getDraftReply = (existingDraft, isReply) => { - let draft = existingDraft ? existingDraft : { - type: 'TextualBody', value: '', draft: true + const purpose = isReply ? 'replying' : 'commenting'; + return existingDraft ? existingDraft : { + type: 'TextualBody', value: '', purpose, draft: true }; - draft.purpose = draft.purpose ? draft.purpose : isReply ? 'replying' : 'commenting'; - return draft; }; /** * Renders a list of comment bodies, followed by a 'reply' field. */ const CommentWidget = props => { - // All comments (draft + non-draft) + // All comments const all = props.annotation ? - props.annotation.bodies.filter(body => isComment(body, props.purpose)) : []; - // Last draft comment without a creator field goes into the reply field - const draftReply = getDraftReply(all.slice().reverse().find(b => b.draft && !b.creator), all.length > 1); + props.annotation.bodies.filter(body => isComment(body, props.purposeSelector)) : []; + + // Add a draft reply if there isn't one already + const draftReply = getDraftReply(all.find(b => b.draft == true), all.length > 1); + // All except draft reply const comments = all.filter(b => b != draftReply); + const onEditReply = evt => { const prev = draftReply.value; const updated = evt.target.value; @@ -56,14 +56,8 @@ const CommentWidget = props => { } } - const onUpdatePurpose = evt => { - const updated = evt.value.trim(); - if (draftReply.value == '' && updated.length > 0) { - draftReply.purpose = updated; - } else { - props.onUpdateBody(draftReply, { ...draftReply, purpose: updated }); - } - } + const onChangeReplyPurpose = purpose => + props.onUpdateBody(draftReply, { ...draftReply, purpose: purpose.value }); // A comment should be read-only if: // - the global read-only flag is set @@ -96,7 +90,7 @@ const CommentWidget = props => { { onChange={onEditReply} onSaveAndClose={() => props.onSaveAndClose()} /> - { props.purpose == true && draftReply.value.length > 0 && - 0 && + props.onSaveAndClose()} /> } diff --git a/src/editor/widgets/comment/PurposeDropdown.jsx b/src/editor/widgets/comment/PurposeSelect.jsx similarity index 83% rename from src/editor/widgets/comment/PurposeDropdown.jsx rename to src/editor/widgets/comment/PurposeSelect.jsx index 3937b74..64f44ce 100644 --- a/src/editor/widgets/comment/PurposeDropdown.jsx +++ b/src/editor/widgets/comment/PurposeSelect.jsx @@ -1,7 +1,7 @@ import React from 'preact/compat'; import Select from 'react-select'; -export const purposes = [ +export const PURPOSES = [ {'value': 'assessing', 'label': 'Assessing'}, {'value': 'bookmarking', 'label': 'Bookmarking'}, {'value': 'classifying', 'label': 'Classifying'}, @@ -16,17 +16,17 @@ export const purposes = [ {'value': 'replying', 'label': 'Replying'} ] -const PurposeDropdown = props => { +const PurposeSelect = props => { const selectedOption = props.content ? - purposes.find(p => p.value === props.content) : null; + PURPOSES.find(p => p.value === props.content) : null; return (