@@ -11,10 +11,6 @@ import { ActivationCheckCallback, EditorActivationCheckFilterObject, FilterHandl
11
11
export interface EditorPluginProps {
12
12
/** The ID of the window to show the editor plugin. Use `undefined` for the main window. */
13
13
windowId : string | undefined ;
14
- /**
15
- * Called to determine whether the custom editor supports the current note.
16
- */
17
- onActivationCheck : ActivationCheckCallback ;
18
14
}
19
15
20
16
export interface SaveEditorContentProps {
@@ -31,6 +27,8 @@ export interface SaveEditorContentProps {
31
27
body : string ;
32
28
}
33
29
30
+ type ActivationCheckSlice = Pick < EditorActivationCheckFilterObject , 'effectiveNoteId' | 'windowId' | 'activatedEditors' > ;
31
+
34
32
/**
35
33
* Allows creating alternative note editors. You can create a view to handle loading and saving the
36
34
* note, and do your own rendering.
@@ -74,6 +72,7 @@ export default class JoplinViewsEditors {
74
72
private store : any ;
75
73
private plugin : Plugin ;
76
74
private activationCheckHandlers_ : Record < string , FilterHandler < EditorActivationCheckFilterObject > > = { } ;
75
+ private unhandledActivationCheck_ : Map < string , ActivationCheckSlice > = new Map ( ) ;
77
76
78
77
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
79
78
public constructor ( plugin : Plugin , store : any ) {
@@ -92,19 +91,40 @@ export default class JoplinViewsEditors {
92
91
id : string ,
93
92
options : EditorPluginProps = {
94
93
windowId : undefined ,
95
- onActivationCheck : async ( ) => false ,
96
94
} ,
97
95
) : Promise < ViewHandle > {
98
96
const windowId = options . windowId ?? defaultWindowId ;
99
97
const handle = createViewHandle ( this . plugin , `${ id } -${ windowId } ` ) ;
100
98
101
- const controller = new WebviewController ( handle , this . plugin . id , this . store , this . plugin . baseDir , ContainerType . Editor , windowId ) ;
102
- this . plugin . addViewController ( controller ) ;
103
- // Restore the last open/closed state for the editor
104
- controller . setOpened ( Setting . value ( 'plugins.shownEditorViewIds' ) . includes ( handle ) ) ;
99
+ const initializeController = ( ) => {
100
+ const controller = new WebviewController ( handle , this . plugin . id , this . store , this . plugin . baseDir , ContainerType . Editor , windowId ) ;
101
+ this . plugin . addViewController ( controller ) ;
102
+ // Restore the last open/closed state for the editor
103
+ controller . setOpened ( Setting . value ( 'plugins.shownEditorViewIds' ) . includes ( handle ) ) ;
104
+ } ;
105
+ // Register the activation check handler early to handle the case where the editorActivationCheck
106
+ // event is fired **before** an activation check handler is registered through the API.
107
+ const registerActivationCheckHandler = ( ) => {
108
+ const onActivationCheck : FilterHandler < EditorActivationCheckFilterObject > = async object => {
109
+ if ( this . activationCheckHandlers_ [ handle ] ) {
110
+ return this . activationCheckHandlers_ [ handle ] ( object ) ;
111
+ } else {
112
+ this . unhandledActivationCheck_ . set ( handle , {
113
+ ...object ,
114
+ } ) ;
115
+ return object ;
116
+ }
117
+ } ;
118
+ eventManager . filterOn ( 'editorActivationCheck' , onActivationCheck ) ;
119
+ this . plugin . addOnUnloadListener ( ( ) => {
120
+ eventManager . filterOff ( 'editorActivationCheck' , onActivationCheck ) ;
121
+ this . unhandledActivationCheck_ . delete ( handle ) ;
122
+ } ) ;
123
+ } ;
124
+
125
+ initializeController ( ) ;
126
+ registerActivationCheckHandler ( ) ;
105
127
106
- // Call onActivationCheck immediately to prevent race conditions.
107
- await this . onActivationCheck ( handle , options . onActivationCheck ) ;
108
128
return handle ;
109
129
}
110
130
@@ -147,26 +167,31 @@ export default class JoplinViewsEditors {
147
167
* `true`, otherwise return `false`.
148
168
*/
149
169
public async onActivationCheck ( handle : ViewHandle , callback : ActivationCheckCallback ) : Promise < void > {
150
- const handler : FilterHandler < EditorActivationCheckFilterObject > = async ( object ) => {
151
- const isCorrectWindow = object . windowId === this . controller ( handle ) . parentWindowId ;
152
- const isActive = isCorrectWindow && await callback ( {
153
- noteId : object . effectiveNoteId ,
154
- windowId : object . windowId ,
170
+ const isActive = async ( { windowId , effectiveNoteId } : ActivationCheckSlice ) => {
171
+ const isCorrectWindow = windowId === this . controller ( handle ) . parentWindowId ;
172
+ const active = isCorrectWindow && await callback ( {
173
+ noteId : effectiveNoteId ,
174
+ windowId : windowId ,
155
175
} ) ;
176
+ return active ;
177
+ } ;
178
+ const handler = async ( object : ActivationCheckSlice ) => {
156
179
object . activatedEditors . push ( {
157
180
pluginId : this . plugin . id ,
158
181
viewId : handle ,
159
- isActive : isActive ,
182
+ isActive : await isActive ( object ) ,
160
183
} ) ;
161
184
return object ;
162
185
} ;
163
186
164
187
this . activationCheckHandlers_ [ handle ] = handler ;
165
188
166
- eventManager . filterOn ( 'editorActivationCheck' , this . activationCheckHandlers_ [ handle ] ) ;
167
- this . plugin . addOnUnloadListener ( ( ) => {
168
- eventManager . filterOff ( 'editorActivationCheck' , this . activationCheckHandlers_ [ handle ] ) ;
169
- } ) ;
189
+ // Handle the case where an activation check was done before the onActivationCheck handler was registered.
190
+ if ( this . unhandledActivationCheck_ . has ( handle ) ) {
191
+ const activationCheckObject = this . unhandledActivationCheck_ . get ( handle ) ;
192
+ this . unhandledActivationCheck_ . delete ( handle ) ;
193
+ this . controller ( handle ) . setActive ( await isActive ( activationCheckObject ) ) ;
194
+ }
170
195
}
171
196
172
197
/**
0 commit comments