Cleanup
This commit is contained in:
parent
ae07174ea5
commit
ac1e27678d
|
@ -41,8 +41,8 @@ const Editor = props => {
|
||||||
// on move. Therefore, don't update if a) props.annotation equals
|
// on move. Therefore, don't update if a) props.annotation equals
|
||||||
// the currentAnnotation, or props.annotation and currentAnnotations are
|
// the currentAnnotation, or props.annotation and currentAnnotations are
|
||||||
// a selection, just created by the user.
|
// a selection, just created by the user.
|
||||||
const preventUpdate = setCurrentAnnotation.isSelection ?
|
const preventUpdate = currentAnnotation?.isSelection ?
|
||||||
annotation.isSelection : currentAnnotation.annotationId === annotation.annotationId;
|
annotation?.isSelection : currentAnnotation?.id === annotation.id;
|
||||||
|
|
||||||
if (!preventUpdate)
|
if (!preventUpdate)
|
||||||
setCurrentAnnotation(annotation);
|
setCurrentAnnotation(annotation);
|
||||||
|
@ -77,9 +77,8 @@ const Editor = props => {
|
||||||
const { user } = props.env;
|
const { user } = props.env;
|
||||||
|
|
||||||
// Metadata is only added when a user is set, otherwise
|
// Metadata is only added when a user is set, otherwise
|
||||||
// the Editor operates in 'anonymous mode'. Also,
|
// the Editor operates in 'anonymous mode'.
|
||||||
// no point in adding meta while we're in draft state
|
if (user) {
|
||||||
if (!body.draft && user) {
|
|
||||||
meta.creator = {};
|
meta.creator = {};
|
||||||
if (user.id) meta.creator.id = user.id;
|
if (user.id) meta.creator.id = user.id;
|
||||||
if (user.displayName) meta.creator.name = user.displayName;
|
if (user.displayName) meta.creator.name = user.displayName;
|
||||||
|
@ -116,11 +115,10 @@ const Editor = props => {
|
||||||
props.onCancel(currentAnnotation);
|
props.onCancel(currentAnnotation);
|
||||||
|
|
||||||
const onOk = _ => {
|
const onOk = _ => {
|
||||||
// Removes the 'draft' flag from all bodies
|
// Removes the state payload from all bodies
|
||||||
const undraft = annotation =>
|
const undraft = annotation =>
|
||||||
annotation.clone({
|
annotation.clone({
|
||||||
body : annotation.bodies.map(({ draft, ...rest }) =>
|
body : annotation.bodies.map(({ draft, ...rest }) => rest)
|
||||||
draft ? { ...rest, ...creationMeta(rest) } : rest )
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Current annotation is either a selection (if it was created from
|
// Current annotation is either a selection (if it was created from
|
||||||
|
|
|
@ -3,11 +3,10 @@ import { useState } from 'preact/hooks';
|
||||||
import TimeAgo from 'timeago-react';
|
import TimeAgo from 'timeago-react';
|
||||||
import DropdownMenu from './DropdownMenu';
|
import DropdownMenu from './DropdownMenu';
|
||||||
import TextEntryField from './TextEntryField';
|
import TextEntryField from './TextEntryField';
|
||||||
import PurposeDropdown from './PurposeDropdown';
|
import PurposeSelect from './PurposeSelect';
|
||||||
import { ChevronDownIcon } from '../../../Icons';
|
import { ChevronDownIcon } from '../../../Icons';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
|
|
||||||
|
|
||||||
/** A single comment inside the CommentWidget **/
|
/** A single comment inside the CommentWidget **/
|
||||||
const Comment = props => {
|
const Comment = props => {
|
||||||
|
|
||||||
|
@ -15,7 +14,6 @@ const Comment = props => {
|
||||||
const [ isMenuVisible, setIsMenuVisible ] = useState(false);
|
const [ isMenuVisible, setIsMenuVisible ] = useState(false);
|
||||||
|
|
||||||
const onMakeEditable = _ => {
|
const onMakeEditable = _ => {
|
||||||
props.body.draft = true;
|
|
||||||
setIsEditable(true);
|
setIsEditable(true);
|
||||||
setIsMenuVisible(false);
|
setIsMenuVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -25,13 +23,13 @@ const Comment = props => {
|
||||||
setIsMenuVisible(false);
|
setIsMenuVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateComment = evt => {
|
const onUpdateComment = evt =>
|
||||||
props.onUpdate(props.body, { ...props.body, value: evt.target.value });
|
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 });
|
props.onUpdate(props.body, { ...props.body, purpose: evt.value });
|
||||||
}
|
|
||||||
|
const timestamp = props.body.modified || props.body.created;
|
||||||
|
|
||||||
const creatorInfo = props.body.creator &&
|
const creatorInfo = props.body.creator &&
|
||||||
<div className="r6o-lastmodified">
|
<div className="r6o-lastmodified">
|
||||||
|
@ -39,7 +37,7 @@ const Comment = props => {
|
||||||
{ props.body.created &&
|
{ props.body.created &&
|
||||||
<span className="r6o-lastmodified-at">
|
<span className="r6o-lastmodified-at">
|
||||||
<TimeAgo
|
<TimeAgo
|
||||||
datetime={props.env.toClientTime(props.body.created)}
|
datetime={props.env.toClientTime(timestamp)}
|
||||||
locale={i18n.locale()} />
|
locale={i18n.locale()} />
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
@ -58,13 +56,13 @@ const Comment = props => {
|
||||||
onChange={onUpdateComment}
|
onChange={onUpdateComment}
|
||||||
onSaveAndClose={props.onSaveAndClose}
|
onSaveAndClose={props.onSaveAndClose}
|
||||||
/>
|
/>
|
||||||
{ creatorInfo }
|
{ !isEditable && creatorInfo }
|
||||||
|
|
||||||
{ props.purpose &&
|
{ props.purposeSelector &&
|
||||||
<PurposeDropdown
|
<PurposeSelect
|
||||||
editable={isEditable}
|
editable={isEditable}
|
||||||
content={props.body.purpose}
|
content={props.body.purpose}
|
||||||
onChange={onUpdateDropdown}
|
onChange={onChangePurpose}
|
||||||
onSaveAndClose={props.onSaveAndClose}
|
onSaveAndClose={props.onSaveAndClose}
|
||||||
/> }
|
/> }
|
||||||
|
|
||||||
|
|
|
@ -2,47 +2,47 @@ import React from 'preact/compat';
|
||||||
import Comment from './Comment';
|
import Comment from './Comment';
|
||||||
import TextEntryField from './TextEntryField';
|
import TextEntryField from './TextEntryField';
|
||||||
import i18n from '../../../i18n';
|
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
|
* Comments are TextualBodies where the purpose field is either
|
||||||
* blank or 'commenting' or 'replying'
|
* blank or 'commenting' or 'replying'
|
||||||
*/
|
*/
|
||||||
const isComment = (body, purposenabled) =>
|
const isComment = (body, matchAllPurposes) => {
|
||||||
body.type === 'TextualBody' && (
|
const hasMatchingPurpose = matchAllPurposes ?
|
||||||
!body.hasOwnProperty('purpose') || purposeCheck(body.purpose, purposenabled)
|
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
|
* 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) => {
|
const getDraftReply = (existingDraft, isReply) => {
|
||||||
let draft = existingDraft ? existingDraft : {
|
const purpose = isReply ? 'replying' : 'commenting';
|
||||||
type: 'TextualBody', value: '', draft: true
|
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.
|
* Renders a list of comment bodies, followed by a 'reply' field.
|
||||||
*/
|
*/
|
||||||
const CommentWidget = props => {
|
const CommentWidget = props => {
|
||||||
// All comments (draft + non-draft)
|
// All comments
|
||||||
const all = props.annotation ?
|
const all = props.annotation ?
|
||||||
props.annotation.bodies.filter(body => isComment(body, props.purpose)) : [];
|
props.annotation.bodies.filter(body => isComment(body, props.purposeSelector)) : [];
|
||||||
// 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);
|
// 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
|
// All except draft reply
|
||||||
const comments = all.filter(b => b != draftReply);
|
const comments = all.filter(b => b != draftReply);
|
||||||
|
|
||||||
const onEditReply = evt => {
|
const onEditReply = evt => {
|
||||||
const prev = draftReply.value;
|
const prev = draftReply.value;
|
||||||
const updated = evt.target.value;
|
const updated = evt.target.value;
|
||||||
|
@ -56,14 +56,8 @@ const CommentWidget = props => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdatePurpose = evt => {
|
const onChangeReplyPurpose = purpose =>
|
||||||
const updated = evt.value.trim();
|
props.onUpdateBody(draftReply, { ...draftReply, purpose: purpose.value });
|
||||||
if (draftReply.value == '' && updated.length > 0) {
|
|
||||||
draftReply.purpose = updated;
|
|
||||||
} else {
|
|
||||||
props.onUpdateBody(draftReply, { ...draftReply, purpose: updated });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A comment should be read-only if:
|
// A comment should be read-only if:
|
||||||
// - the global read-only flag is set
|
// - the global read-only flag is set
|
||||||
|
@ -96,7 +90,7 @@ const CommentWidget = props => {
|
||||||
<Comment
|
<Comment
|
||||||
key={idx}
|
key={idx}
|
||||||
env={props.env}
|
env={props.env}
|
||||||
purpose={props.purpose}
|
purposeSelector={props.purposeSelector}
|
||||||
readOnly={isReadOnly(body)}
|
readOnly={isReadOnly(body)}
|
||||||
body={body}
|
body={body}
|
||||||
onUpdate={props.onUpdateBody}
|
onUpdate={props.onUpdateBody}
|
||||||
|
@ -113,11 +107,11 @@ const CommentWidget = props => {
|
||||||
onChange={onEditReply}
|
onChange={onEditReply}
|
||||||
onSaveAndClose={() => props.onSaveAndClose()}
|
onSaveAndClose={() => props.onSaveAndClose()}
|
||||||
/>
|
/>
|
||||||
{ props.purpose == true && draftReply.value.length > 0 &&
|
{ props.purposeSelector && draftReply.value.length > 0 &&
|
||||||
<PurposeDropdown
|
<PurposeSelect
|
||||||
editable={true}
|
editable={true}
|
||||||
content={draftReply.purpose}
|
content={draftReply.purpose}
|
||||||
onChange={onUpdatePurpose}
|
onChange={onChangeReplyPurpose}
|
||||||
onSaveAndClose={() => props.onSaveAndClose()}
|
onSaveAndClose={() => props.onSaveAndClose()}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'preact/compat';
|
import React from 'preact/compat';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
|
|
||||||
export const purposes = [
|
export const PURPOSES = [
|
||||||
{'value': 'assessing', 'label': 'Assessing'},
|
{'value': 'assessing', 'label': 'Assessing'},
|
||||||
{'value': 'bookmarking', 'label': 'Bookmarking'},
|
{'value': 'bookmarking', 'label': 'Bookmarking'},
|
||||||
{'value': 'classifying', 'label': 'Classifying'},
|
{'value': 'classifying', 'label': 'Classifying'},
|
||||||
|
@ -16,17 +16,17 @@ export const purposes = [
|
||||||
{'value': 'replying', 'label': 'Replying'}
|
{'value': 'replying', 'label': 'Replying'}
|
||||||
]
|
]
|
||||||
|
|
||||||
const PurposeDropdown = props => {
|
const PurposeSelect = props => {
|
||||||
|
|
||||||
const selectedOption = props.content ?
|
const selectedOption = props.content ?
|
||||||
purposes.find(p => p.value === props.content) : null;
|
PURPOSES.find(p => p.value === props.content) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="r6o-purposedropdown">
|
<div class="r6o-purposedropdown">
|
||||||
<Select
|
<Select
|
||||||
value={selectedOption}
|
value={selectedOption}
|
||||||
onChange={props.onChange}
|
onChange={props.onChange}
|
||||||
options={purposes}
|
options={PURPOSES}
|
||||||
isDisabled={!props.editable}
|
isDisabled={!props.editable}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,4 +34,4 @@ const PurposeDropdown = props => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PurposeDropdown;
|
export default PurposeSelect;
|
Loading…
Reference in New Issue