1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
/**
* This file allows introducing new JS modules without contaminating other files.
* This is useful when bug fixing requires adding such JS modules in stable
* versions of Odoo. Any module that is defined in this file should be isolated
* in its own file in master.
*/
odoo.define('mail/static/src/bugfix/bugfix.js', function (require) {
'use strict';
});
// Should be moved to its own file in master.
odoo.define('mail/static/src/component_hooks/use_rendered_values/use_rendered_values.js', function (require) {
'use strict';
const { Component } = owl;
const { onMounted, onPatched } = owl.hooks;
/**
* This hooks provides support for accessing the values returned by the given
* selector at the time of the last render. The values will be updated after
* every mount/patch.
*
* @param {function} selector function that will be executed at the time of the
* render and of which the result will be stored for future reference.
* @returns {function} function to call to retrieve the last rendered values.
*/
function useRenderedValues(selector) {
const component = Component.current;
let renderedValues;
let patchedValues;
const __render = component.__render.bind(component);
component.__render = function () {
renderedValues = selector();
return __render(...arguments);
};
onMounted(onUpdate);
onPatched(onUpdate);
function onUpdate() {
patchedValues = renderedValues;
}
return () => patchedValues;
}
return useRenderedValues;
});
// Should be moved to its own file in master.
odoo.define('mail/static/src/component_hooks/use_update/use_update.js', function (require) {
'use strict';
const { Component } = owl;
const { onMounted, onPatched } = owl.hooks;
const executionQueue = [];
function executeNextInQueue() {
if (executionQueue.length === 0) {
return;
}
const { component, func } = executionQueue.shift();
if (component.__owl__.status !== 5 /* DESTROYED */) {
func();
}
executeNextInQueue();
}
/**
* @param {Object} param0
* @param {Component} param0.component
* @param {function} param0.func
* @param {integer} param0.priority
*/
async function addFunctionToQueue({ component, func, priority }) {
const index = executionQueue.findIndex(item => item.priority > priority);
const item = { component, func, priority };
if (index === -1) {
executionQueue.push(item);
} else {
executionQueue.splice(index, 0, item);
}
// Timeout to allow all components to register their function before
// executing any of them, to respect all priorities.
await new Promise(resolve => setTimeout(resolve));
executeNextInQueue();
}
/**
* This hook provides support for executing code after update (render or patch).
*
* @param {Object} param0
* @param {function} param0.func the function to execute after the update.
* @param {integer} [param0.priority] determines the execution order of the function
* among the update function of other components. Lower priority is executed
* first. If no priority is given, the function is executed immediately.
* This param is deprecated because desynchronized update is causing issue if
* there is a new render planned in the meantime (models data become obsolete
* in the update method).
*/
function useUpdate({ func, priority }) {
const component = Component.current;
onMounted(onUpdate);
onPatched(onUpdate);
function onUpdate() {
if (priority === undefined) {
func();
return;
}
addFunctionToQueue({ component, func, priority });
}
}
return useUpdate;
});
// Should be moved to its own file in master.
odoo.define('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js', function (require) {
'use strict';
const { Component } = owl;
/**
* Compares `a` and `b` up to the given `compareDepth`.
*
* @param {any} a
* @param {any} b
* @param {Object|integer} compareDepth
* @returns {boolean}
*/
function isEqual(a, b, compareDepth) {
const keys = Object.keys(a);
if (Object.keys(b).length !== keys.length) {
return false;
}
for (const key of keys) {
// the depth can be given either as a number (for all keys) or as
// an object (for each key)
let depth;
if (typeof compareDepth === 'number') {
depth = compareDepth;
} else {
depth = compareDepth[key] || 0;
}
if (depth === 0 && a[key] !== b[key]) {
return false;
}
if (depth !== 0) {
let nextDepth;
if (typeof depth === 'number') {
nextDepth = depth - 1;
} else {
nextDepth = depth;
}
if (!isEqual(a[key], b[key], nextDepth)) {
return false;
}
}
}
return true;
}
/**
* This hook overrides the `shouldUpdate` method to ensure the component is only
* updated if its props actually changed. This is especially useful to use on
* components for which an extra render costs proportionally a lot more than
* comparing props.
*
* @param {Object} [param0={}]
* @param {Object} [param0.compareDepth={}] allows to specify the comparison
* depth to use for each prop. Default is shallow compare (depth = 0).
*/
function useShouldUpdateBasedOnProps({ compareDepth = {} } = {}) {
const component = Component.current;
component.shouldUpdate = nextProps => {
const allNewProps = Object.assign({}, nextProps);
const defaultProps = component.constructor.defaultProps;
for (const key in defaultProps) {
if (allNewProps[key] === undefined) {
allNewProps[key] = defaultProps[key];
}
}
return !isEqual(component.props, allNewProps, compareDepth);
};
}
return useShouldUpdateBasedOnProps;
});
|