@@ -4,7 +4,6 @@ import { MassMailingIframe } from "@mass_mailing/iframe/mass_mailing_iframe";
4
4
import { ThemeSelector } from "@mass_mailing/themes/theme_selector/theme_selector" ;
5
5
import { onWillUpdateProps , status , toRaw , useEffect , useRef } from "@odoo/owl" ;
6
6
import { useChildRef , useService } from "@web/core/utils/hooks" ;
7
- import { useTransition } from "@web/core/transition" ;
8
7
import { effect } from "@web/core/utils/reactive" ;
9
8
import { htmlField , HtmlField } from "@html_editor/fields/html_field" ;
10
9
import { normalizeHTML , parseHTML } from "@html_editor/utils/html" ;
@@ -41,22 +40,12 @@ export class MassMailingHtmlField extends HtmlField {
41
40
this . iframeRef = useChildRef ( ) ;
42
41
this . codeViewButtonRef = useRef ( "codeViewButtonRef" ) ;
43
42
44
- // Use a transition to display the HtmlField only when the themes
45
- // service finished loading
46
- this . displayTransition = useTransition ( {
47
- name : "mass_mailing_html_field" ,
48
- initialVisibility : false ,
49
- immediate : false ,
50
- leaveDuration : 150 ,
51
- onLeave : ( ) => { } ,
52
- } ) ;
53
-
54
- // Force a full reload for MassMailingIframe on readonly change
55
43
onWillUpdateProps ( ( nextProps ) => {
56
44
if (
57
45
this . props . readonly !== nextProps . readonly &&
58
46
( this . props . readonly || nextProps . readonly )
59
47
) {
48
+ // Force a full reload for MassMailingIframe on readonly change
60
49
this . state . key ++ ;
61
50
}
62
51
if ( nextProps . readonly ) {
@@ -65,27 +54,30 @@ export class MassMailingHtmlField extends HtmlField {
65
54
if ( nextProps . record . isNew ) {
66
55
Object . assign ( toRaw ( this . state ) , {
67
56
activeTheme : undefined ,
57
+ showCodeView : false ,
68
58
showThemeSelector : true ,
69
- showCodeView : true ,
70
59
} ) ;
71
60
}
72
61
} ) ;
73
62
74
- // Recompute the themeOptions when the html value changes on the record
75
63
let currentKey ;
76
64
effect (
77
65
( state ) => {
78
66
if ( status ( this ) === "destroyed" ) {
79
67
return ;
80
68
}
81
69
if ( state . key !== currentKey ) {
82
- this . updateActiveTheme ( ) ;
70
+ // html value may have been reset from the server:
71
+ // - await the new iframe
83
72
this . resetIframe ( ) ;
73
+ // - ensure that the activeTheme is up to date.
74
+ this . updateActiveTheme ( ) ;
84
75
currentKey = state . key ;
85
76
}
86
77
} ,
87
78
[ this . state ]
88
79
) ;
80
+
89
81
// Evaluate if the themeSelector should be displayed
90
82
effect (
91
83
( state ) => {
@@ -95,25 +87,26 @@ export class MassMailingHtmlField extends HtmlField {
95
87
const activeTheme = state . activeTheme ;
96
88
const showThemeSelector = state . showThemeSelector ;
97
89
if ( ! activeTheme && ! showThemeSelector && ! this . props . readonly ) {
98
- // Always show the theme selector when the theme is unknown
90
+ // Show the ThemeSelector when the theme is unknown and the content can be
91
+ // changed (invalid value).
99
92
state . showThemeSelector = true ;
100
93
} else if ( ( activeTheme && showThemeSelector ) || this . props . readonly ) {
101
94
state . showThemeSelector = false ;
102
95
}
103
96
if ( showThemeSelector && toRaw ( state . showCodeView ) ) {
104
- // Ensure that the code view is always disabled when the theme selector
105
- // is displayed
97
+ // Never show the CodeView with the ThemeSelector.
106
98
state . showCodeView = false ;
107
99
}
108
100
} ,
109
101
[ this . state ]
110
102
) ;
111
- // Sets the iframe height
103
+
112
104
useEffect (
113
105
( ) => {
114
106
if ( ! this . codeViewRef . el ) {
115
107
return ;
116
108
}
109
+ // Set the initial textArea height.
117
110
this . codeViewRef . el . style . height = this . codeViewRef . el . scrollHeight + "px" ;
118
111
} ,
119
112
( ) => [ this . codeViewRef . el ]
@@ -129,6 +122,10 @@ export class MassMailingHtmlField extends HtmlField {
129
122
this . lastIframeLoaded = this . keepLastIframe . add ( this . iframeLoaded ) ;
130
123
}
131
124
125
+ onIframeLoad ( iframeLoaded ) {
126
+ this . iframeLoaded . resolve ( iframeLoaded ) ;
127
+ }
128
+
132
129
updateActiveTheme ( ) {
133
130
const activeTheme = this . themeService . getThemeName ( this . value ) ;
134
131
if ( toRaw ( this . state ) . activeTheme !== activeTheme ) {
@@ -157,18 +154,15 @@ export class MassMailingHtmlField extends HtmlField {
157
154
}
158
155
159
156
/**
160
- * Contrary to the super method, toggling the codeView in mass_mailing
161
- * does not keep the editor alive, as the editor does properly support
162
- * reinsertion. Instead, a new editor will be reconstructed from the
163
- * codeView value, and all plugins will be instantiated from scratch.
157
+ * Content reinsertion as done in the super method is not properly supported
158
+ * by the editor (corrupted state). This override forces the creation of
159
+ * a new editor instead, and all plugins will be instantiated from scratch.
164
160
* @override
165
161
*/
166
162
async toggleCodeView ( ) {
167
163
await this . commitChanges ( ) ;
168
164
this . state . showCodeView = ! this . state . showCodeView ;
169
- if ( ! this . state . showCodeView && this . editor ) {
170
- this . state . key ++ ;
171
- }
165
+ this . state . key ++ ;
172
166
}
173
167
174
168
/**
@@ -193,22 +187,26 @@ export class MassMailingHtmlField extends HtmlField {
193
187
194
188
getBuilderConfig ( ) {
195
189
const config = super . getConfig ( ) ;
196
- // All plugins for the html builder are defined in mass_mailing_builder
190
+ // All plugins for the html Builder are defined in MassMailingBuilder
197
191
delete config . Plugins ;
198
192
return {
199
193
...config ,
200
- allowMediaDialogVideo : false , // videos aren't allowed in mails
201
- withBuilder : true ,
202
- toggleThemeSelector : ( show ) => this . toggleThemeSelector ( show ) ,
203
194
mobileBreakpoint : "md" ,
204
195
} ;
205
196
}
206
197
207
198
getSimpleEditorConfig ( ) {
199
+ const config = super . getConfig ( ) ;
200
+ const codeViewCommand = [ config . resources ?. user_commands ]
201
+ . filter ( Boolean )
202
+ . flat ( )
203
+ . find ( ( cmd ) => cmd . id === "codeview" ) ;
204
+ if ( codeViewCommand ) {
205
+ codeViewCommand . isAvailable = ( ) => this . env . debug ;
206
+ }
208
207
return {
209
- ...super . getConfig ( ) ,
208
+ ...config ,
210
209
Plugins : [ ...MAIN_EDITOR_PLUGINS , DynamicPlaceholderPlugin ] ,
211
- toggleThemeSelector : ( show ) => this . toggleThemeSelector ( show ) ,
212
210
} ;
213
211
}
214
212
@@ -224,33 +222,15 @@ export class MassMailingHtmlField extends HtmlField {
224
222
} ;
225
223
}
226
224
227
- onIframeLoad ( iframeLoaded ) {
228
- this . iframeLoaded . resolve ( iframeLoaded ) ;
229
- }
230
-
231
- toggleThemeSelector ( show = true ) {
232
- this . state . showThemeSelector = show ;
233
- }
234
-
235
- /**
236
- * Process the content at a specifically designed location to avoid
237
- * interference with the UI.
238
- * @override
239
- */
240
- insertForInlineProcessing ( el ) {
241
- const processingContainer = this . iframeRef . el . contentDocument . querySelector (
242
- ".o_mass_mailing_processing_container"
243
- ) ;
244
- processingContainer . append ( el ) ;
245
- }
246
-
247
225
onTextareaInput ( ev ) {
226
+ this . onChange ( ) ;
248
227
ev . target . style . height = ev . target . scrollHeight + "px" ;
249
228
}
250
229
251
230
/**
252
231
* Ensure that the inlineField has its first value set (in case the
253
- * body_arch was set manually without using this widget).
232
+ * field value was set manually without using this widget).
233
+ * @override
254
234
*/
255
235
async _commitChanges ( { urgent } ) {
256
236
if (
@@ -269,20 +249,21 @@ export class MassMailingHtmlField extends HtmlField {
269
249
* Complete rewrite of `updateValue` to ensure that both the field and the
270
250
* inlineField are saved at the same time. Depends on the iframe to compute
271
251
* the style of the inlineField.
272
- * TODO EGGMAIL: this is too slow for urgent save. We should display the
273
- * save confirmation popup like in website on beforeUnload, unlike other
274
- * html_fields in form views.
275
252
* @override
276
253
*/
277
254
async updateValue ( value ) {
278
255
await this . lastIframeLoaded ;
279
256
this . lastValue = normalizeHTML ( value , this . clearElementToCompare . bind ( this ) ) ;
280
257
this . isDirty = false ;
281
258
const shouldRestoreDisplayNone = this . iframeRef . el . classList . contains ( "d-none" ) ;
259
+ // d-none must be removed for style computation.
282
260
this . iframeRef . el . classList . remove ( "d-none" ) ;
283
261
const processingEl = this . iframeRef . el . contentDocument . createElement ( "DIV" ) ;
284
262
processingEl . append ( parseHTML ( this . iframeRef . el . contentDocument , value ) ) ;
285
- this . insertForInlineProcessing ( processingEl ) ;
263
+ const processingContainer = this . iframeRef . el . contentDocument . querySelector (
264
+ ".o_mass_mailing_processing_container"
265
+ ) ;
266
+ processingContainer . append ( processingEl ) ;
286
267
const cssRules = getCSSRules ( this . iframeRef . el . contentDocument ) ;
287
268
await toInline ( processingEl , cssRules ) ;
288
269
const inlineValue = processingEl . innerHTML ;
@@ -305,11 +286,16 @@ export const massMailingHtmlField = {
305
286
component : MassMailingHtmlField ,
306
287
extractProps ( { attrs, options } ) {
307
288
const props = htmlField . extractProps ( ...arguments ) ;
289
+ props . editorConfig . allowVideo = false ;
290
+ props . editorConfig . baseContainers = [ "P" ] ;
308
291
Object . assign ( props , {
309
292
filterTemplates : options . filterTemplates ,
310
293
inlineField : options [ "inline_field" ] ,
311
294
migrateHTML : false ,
312
295
embeddedComponents : false ,
296
+ isCollaborative : false ,
297
+ sandboxedPreview : false ,
298
+ codeview : true ,
313
299
} ) ;
314
300
return props ;
315
301
} ,
0 commit comments