1
1
import { normalizeKeyName } from '@hypothesis/frontend-shared' ;
2
- import { useState } from 'preact/hooks' ;
2
+ import { useCallback , useState } from 'preact/hooks' ;
3
3
4
4
import { withServices } from '../../service-context' ;
5
+ import { isReply , isSaved } from '../../helpers/annotation-metadata' ;
5
6
import { applyTheme } from '../../helpers/theme' ;
6
7
import { useStoreProxy } from '../../store/use-store' ;
7
8
@@ -13,12 +14,14 @@ import AnnotationPublishControl from './AnnotationPublishControl';
13
14
14
15
/**
15
16
* @typedef {import("../../../types/api").Annotation } Annotation
17
+ * @typedef {import("../../store/modules/drafts").Draft } Draft
16
18
* @typedef {import("../../../types/config").SidebarSettings } SidebarSettings
17
19
*/
18
20
19
21
/**
20
22
* @typedef AnnotationEditorProps
21
23
* @prop {Annotation } annotation - The annotation under edit
24
+ * @prop {Draft } draft - The annotation's draft
22
25
* @prop {import('../../services/annotations').AnnotationsService } annotationsService
23
26
* @prop {SidebarSettings } settings - Injected service
24
27
* @prop {import('../../services/toast-messenger').ToastMessengerService } toastMessenger
@@ -32,6 +35,7 @@ import AnnotationPublishControl from './AnnotationPublishControl';
32
35
*/
33
36
function AnnotationEditor ( {
34
37
annotation,
38
+ draft,
35
39
annotationsService,
36
40
settings,
37
41
tags : tagsService ,
@@ -43,63 +47,83 @@ function AnnotationEditor({
43
47
) ;
44
48
45
49
const store = useStoreProxy ( ) ;
46
- const draft = store . getDraft ( annotation ) ;
47
50
const group = store . getGroup ( annotation . group ) ;
48
51
49
- if ( ! draft ) {
50
- // If there's no draft, we can't be in editing mode
51
- return null ;
52
- }
53
-
54
52
const shouldShowLicense =
55
53
! draft . isPrivate && group && group . type !== 'private' ;
56
54
57
55
const tags = draft . tags ;
58
56
const text = draft . text ;
59
57
const isEmpty = ! text && ! tags . length ;
60
58
61
- const onEditTags = ( { tags } ) => {
62
- store . createDraft ( draft . annotation , { ...draft , tags } ) ;
63
- } ;
59
+ const onEditTags = useCallback (
60
+ ( { tags } ) => {
61
+ store . createDraft ( draft . annotation , { ...draft , tags } ) ;
62
+ } ,
63
+ [ draft , store ]
64
+ ) ;
64
65
65
66
/**
66
67
* Verify `newTag` has content and is not a duplicate; add the tag
67
68
*
68
69
* @param {string } newTag
69
70
* @return {boolean } - `true` if tag is added
70
71
*/
71
- const onAddTag = newTag => {
72
- if ( ! newTag || tags . indexOf ( newTag ) >= 0 ) {
73
- // don't add empty or duplicate tags
74
- return false ;
75
- }
76
- const tagList = [ ...tags , newTag ] ;
77
- // Update the tag locally for the suggested-tag list
78
- tagsService . store ( tagList ) ;
79
- onEditTags ( { tags : tagList } ) ;
80
- return true ;
81
- } ;
72
+ const onAddTag = useCallback (
73
+ newTag => {
74
+ if ( ! newTag || tags . indexOf ( newTag ) >= 0 ) {
75
+ // don't add empty or duplicate tags
76
+ return false ;
77
+ }
78
+ const tagList = [ ...tags , newTag ] ;
79
+ // Update the tag locally for the suggested-tag list
80
+ tagsService . store ( tagList ) ;
81
+ onEditTags ( { tags : tagList } ) ;
82
+ return true ;
83
+ } ,
84
+ [ onEditTags , tags , tagsService ]
85
+ ) ;
82
86
83
87
/**
84
88
* Remove a tag from the annotation.
85
89
*
86
90
* @param {string } tag
87
91
* @return {boolean } - `true` if tag extant and removed
88
92
*/
89
- const onRemoveTag = tag => {
90
- const newTagList = [ ...tags ] ; // make a copy
91
- const index = newTagList . indexOf ( tag ) ;
92
- if ( index >= 0 ) {
93
- newTagList . splice ( index , 1 ) ;
94
- onEditTags ( { tags : newTagList } ) ;
95
- return true ;
96
- }
97
- return false ;
98
- } ;
93
+ const onRemoveTag = useCallback (
94
+ tag => {
95
+ const newTagList = [ ...tags ] ; // make a copy
96
+ const index = newTagList . indexOf ( tag ) ;
97
+ if ( index >= 0 ) {
98
+ newTagList . splice ( index , 1 ) ;
99
+ onEditTags ( { tags : newTagList } ) ;
100
+ return true ;
101
+ }
102
+ return false ;
103
+ } ,
104
+ [ onEditTags , tags ]
105
+ ) ;
99
106
100
- const onEditText = ( { text } ) => {
101
- store . createDraft ( draft . annotation , { ...draft , text } ) ;
102
- } ;
107
+ const onEditText = useCallback (
108
+ ( { text } ) => {
109
+ store . createDraft ( draft . annotation , { ...draft , text } ) ;
110
+ } ,
111
+ [ draft , store ]
112
+ ) ;
113
+
114
+ /**
115
+ * @param {boolean } isPrivate
116
+ */
117
+ const onSetPrivacy = useCallback (
118
+ isPrivate => {
119
+ store . createDraft ( annotation , { ...draft , isPrivate } ) ;
120
+ // Persist this as privacy default for future annotations unless this is a reply
121
+ if ( ! isReply ( annotation ) ) {
122
+ store . setDefault ( 'annotationPrivacy' , isPrivate ? 'private' : 'shared' ) ;
123
+ }
124
+ } ,
125
+ [ annotation , draft , store ]
126
+ ) ;
103
127
104
128
const onSave = async ( ) => {
105
129
// If there is any content in the tag editor input field that has
@@ -115,6 +139,14 @@ function AnnotationEditor({
115
139
}
116
140
} ;
117
141
142
+ // Revert changes to this annotation
143
+ const onCancel = useCallback ( ( ) => {
144
+ store . removeDraft ( annotation ) ;
145
+ if ( ! isSaved ( annotation ) ) {
146
+ store . removeAnnotations ( [ annotation ] ) ;
147
+ }
148
+ } , [ annotation , store ] ) ;
149
+
118
150
// Allow saving of annotation by pressing CMD/CTRL-Enter
119
151
/** @param {KeyboardEvent } event */
120
152
const onKeyDown = event => {
@@ -150,11 +182,16 @@ function AnnotationEditor({
150
182
tagList = { tags }
151
183
/>
152
184
< div className = "hyp-u-layout-row annotation__form-actions" >
153
- < AnnotationPublishControl
154
- annotation = { annotation }
155
- isDisabled = { isEmpty }
156
- onSave = { onSave }
157
- />
185
+ { group && (
186
+ < AnnotationPublishControl
187
+ group = { group }
188
+ isDisabled = { isEmpty }
189
+ isPrivate = { draft . isPrivate }
190
+ onCancel = { onCancel }
191
+ onSave = { onSave }
192
+ onSetPrivacy = { onSetPrivacy }
193
+ />
194
+ ) }
158
195
</ div >
159
196
{ shouldShowLicense && < AnnotationLicense /> }
160
197
</ div >
0 commit comments