diff --git a/addons/mail/static/src/component_hooks/use_models.js b/addons/mail/static/src/component_hooks/use_models.js
index ca2e48c2845..82f93ff307e 100644
--- a/addons/mail/static/src/component_hooks/use_models.js
+++ b/addons/mail/static/src/component_hooks/use_models.js
@@ -15,31 +15,21 @@ const { onRendered, onWillDestroy, onWillRender, useComponent } = owl;
*/
export function useModels() {
const component = useComponent();
- const { modelManager } = component.env.services.messaging;
- Object.defineProperty(component, 'messaging', {
- get: () => modelManager.messaging,
- });
const listener = new Listener({
isLocking: false, // unfortunately __render has side effects such as children components updating their reference to their corresponding model
name: `useModels() of ${component}`,
onChange: () => component.render(),
});
onWillRender(() => {
- if (modelManager) {
- modelManager.startListening(listener);
- }
+ component.env.services.messaging.modelManager.startListening(listener);
});
onRendered(() => {
- if (modelManager) {
- modelManager.stopListening(listener);
- }
+ component.env.services.messaging.modelManager.stopListening(listener);
});
onWillDestroy(() => {
- if (modelManager) {
- modelManager.removeListener(listener);
- }
+ component.env.services.messaging.modelManager.removeListener(listener);
});
- modelManager.messagingCreatedPromise.then(() => {
+ component.env.services.messaging.modelManager.messagingCreatedPromise.then(() => {
component.render();
});
}
diff --git a/addons/mail/static/src/component_hooks/use_rendered_values.js b/addons/mail/static/src/component_hooks/use_rendered_values.js
index c8840e31f15..e0fe62179e3 100644
--- a/addons/mail/static/src/component_hooks/use_rendered_values.js
+++ b/addons/mail/static/src/component_hooks/use_rendered_values.js
@@ -17,21 +17,20 @@ export function useRenderedValues(selector) {
const component = useComponent();
let renderedValues;
let patchedValues;
- const { modelManager } = component.env.services.messaging;
const listener = new Listener({
name: `useRenderedValues() of ${component}`,
onChange: () => component.render(),
});
onWillRender(() => {
- modelManager.startListening(listener);
+ component.env.services.messaging.modelManager.startListening(listener);
renderedValues = selector();
- modelManager.stopListening(listener);
+ component.env.services.messaging.modelManager.stopListening(listener);
});
onMounted(onUpdate);
onPatched(onUpdate);
function onUpdate() {
patchedValues = renderedValues;
}
- onWillDestroy(() => modelManager.removeListener(listener));
+ onWillDestroy(() => component.env.services.messaging.modelManager.removeListener(listener));
return () => patchedValues;
}
diff --git a/addons/mail/static/src/component_hooks/use_update.js b/addons/mail/static/src/component_hooks/use_update.js
index 5a06ee1b26f..85034868620 100644
--- a/addons/mail/static/src/component_hooks/use_update.js
+++ b/addons/mail/static/src/component_hooks/use_update.js
@@ -12,26 +12,19 @@ const { onMounted, onPatched, onWillDestroy, useComponent } = owl;
*/
export function useUpdate({ func }) {
const component = useComponent();
- const { modelManager } = component.env.services.messaging;
const listener = new Listener({
isLocking: false, // unfortunately onUpdate methods often have side effect
name: `useUpdate() of ${component}`,
onChange: () => component.render(),
});
function onUpdate() {
- if (modelManager) {
- modelManager.startListening(listener);
- }
+ component.env.services.messaging.modelManager.startListening(listener);
func();
- if (modelManager) {
- modelManager.stopListening(listener);
- }
+ component.env.services.messaging.modelManager.stopListening(listener);
}
onMounted(onUpdate);
onPatched(onUpdate);
onWillDestroy(() => {
- if (modelManager) {
- modelManager.removeListener(listener);
- }
+ component.env.services.messaging.modelManager.removeListener(listener);
});
}
diff --git a/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js b/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js
index c3e33d48183..e5f5b64ba43 100644
--- a/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js
+++ b/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js
@@ -16,6 +16,10 @@ export class ChatWindowManagerContainer extends Component {
useModels();
super.setup();
}
+
+ get messaging() {
+ return this.env.services.messaging.modelManager.messaging;
+ }
}
Object.assign(ChatWindowManagerContainer, {
diff --git a/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.xml b/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.xml
index a7ddd7d94d1..390f2ccf5b7 100644
--- a/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.xml
+++ b/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js b/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js
index 95c30e326f8..2024aef1cd0 100644
--- a/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js
+++ b/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js
@@ -16,6 +16,10 @@ export class DialogManagerContainer extends Component {
useModels();
super.setup();
}
+
+ get messaging() {
+ return this.env.services.messaging.modelManager.messaging;
+ }
}
Object.assign(DialogManagerContainer, {
diff --git a/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.xml b/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.xml
index 1f730a30ae0..47e27238e44 100644
--- a/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.xml
+++ b/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/addons/mail/static/src/components/discuss_container/discuss_container.js b/addons/mail/static/src/components/discuss_container/discuss_container.js
index f2dd2503384..bd506cbaa15 100644
--- a/addons/mail/static/src/components/discuss_container/discuss_container.js
+++ b/addons/mail/static/src/components/discuss_container/discuss_container.js
@@ -53,6 +53,10 @@ export class DiscussContainer extends Component {
DiscussContainer.currentInstance = this;
}
+ get messaging() {
+ return this.env.services.messaging.modelManager.messaging;
+ }
+
_willDestroy() {
if (this.discuss && DiscussContainer.currentInstance === this) {
this.discuss.close();
diff --git a/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.js b/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.js
index 3c232df6b3f..d2e4c2e3e7e 100644
--- a/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.js
+++ b/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.js
@@ -18,7 +18,11 @@ export class DiscussPublicViewContainer extends Component {
}
get discussPublicView() {
- return this.messaging.models['DiscussPublicView'].findFromIdentifyingData(this.messaging);
+ return this.messaging && this.messaging.models['DiscussPublicView'].findFromIdentifyingData(this.messaging);
+ }
+
+ get messaging() {
+ return this.env.services.messaging.modelManager.messaging;
}
}
diff --git a/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.xml b/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.xml
index 690bb3bc247..03099cdd2e7 100644
--- a/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.xml
+++ b/addons/mail/static/src/components/discuss_public_view_container/discuss_public_view_container.xml
@@ -2,10 +2,10 @@
diff --git a/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js b/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js
index efa9ab2064d..5a45b20a226 100644
--- a/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js
+++ b/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js
@@ -17,6 +17,9 @@ export class MessagingMenuContainer extends Component {
super.setup();
}
+ get messaging() {
+ return this.env.services.messaging.modelManager.messaging;
+ }
}
Object.assign(MessagingMenuContainer, {
diff --git a/addons/mail/static/src/model/model_manager.js b/addons/mail/static/src/model/model_manager.js
index 0613ea38c88..1750a585feb 100644
--- a/addons/mail/static/src/model/model_manager.js
+++ b/addons/mail/static/src/model/model_manager.js
@@ -195,6 +195,24 @@ export class ModelManager {
this._flushUpdateCycle();
}
+ /**
+ * Destroys this model manager, which consists of cleaning all possible
+ * references in order to avoid memory leaks.
+ */
+ destroy() {
+ this.messaging.delete();
+ for (const model of Object.values(this.models)) {
+ delete model.__fieldList;
+ delete model.__fieldMap;
+ delete model.__identifyingFields;
+ delete model.__identifyingFieldsFlattened;
+ delete model.__records;
+ delete model.__requiredFieldsList;
+ delete model.fields;
+ delete model.modelManager;
+ }
+ }
+
/**
* Returns whether the given record still exists.
*
@@ -1216,13 +1234,14 @@ export class ModelManager {
field => field.required
);
// Add field accessors.
- for (const field of model.__fieldList) {
- Object.defineProperty(model.prototype, field.fieldName, {
+ for (const fieldName of Object.keys(model.__fieldMap)) {
+ Object.defineProperty(model.prototype, fieldName, {
get: function getFieldValue() { // this is bound to record
if (!this.exists()) {
// Deprecated, allows reading fields on deleted records. Use exists() instead.
return undefined;
}
+ const field = model.__fieldMap[fieldName];
if (this.modelManager._listeners.size) {
if (!this.modelManager._listenersObservingLocalId.has(this.localId)) {
this.modelManager._listenersObservingLocalId.set(this.localId, new Map());
diff --git a/addons/mail/static/src/models/record.js b/addons/mail/static/src/models/record.js
index 2c1ad806f1f..4a1dd387806 100644
--- a/addons/mail/static/src/models/record.js
+++ b/addons/mail/static/src/models/record.js
@@ -125,7 +125,7 @@ registerModel({
* @returns {Messaging}
*/
messaging() {
- return this.modelManager.messaging;
+ return this.modelManager && this.modelManager.messaging;
},
/**
* Returns all existing models.
@@ -149,6 +149,9 @@ registerModel({
* @returns {boolean}
*/
exists() {
+ if (!this.modelManager) {
+ return false;
+ }
return this.modelManager.exists(this.constructor, this);
},
/**
diff --git a/addons/mail/static/src/utils/messaging_component.js b/addons/mail/static/src/utils/messaging_component.js
index 6d0129d9741..97e5e0f8b9d 100644
--- a/addons/mail/static/src/utils/messaging_component.js
+++ b/addons/mail/static/src/utils/messaging_component.js
@@ -44,6 +44,9 @@ export function registerMessagingComponent(ComponentClass) {
}
return res;
}
+ get messaging() {
+ return this.env.services.messaging.modelManager.messaging;
+ }
/**
* @returns {string}
*/
diff --git a/addons/mail/static/tests/helpers/test_utils.js b/addons/mail/static/tests/helpers/test_utils.js
index 6767b490c5c..6f8806526df 100644
--- a/addons/mail/static/tests/helpers/test_utils.js
+++ b/addons/mail/static/tests/helpers/test_utils.js
@@ -406,9 +406,9 @@ function getCreateChatterContainerComponent({ afterEvent, env, target }) {
};
}
-function getCreateComposerComponent({ env, modelManager, target }) {
+function getCreateComposerComponent({ env, target }) {
return async function createComposerComponent(composer, props) {
- const composerView = modelManager.messaging.models['ComposerView'].create({
+ const composerView = env.services.messaging.modelManager.messaging.models['ComposerView'].create({
qunitTest: insertAndReplace({
composer: replace(composer),
}),
@@ -420,9 +420,9 @@ function getCreateComposerComponent({ env, modelManager, target }) {
};
}
-function getCreateComposerSuggestionComponent({ env, modelManager, target }) {
+function getCreateComposerSuggestionComponent({ env, target }) {
return async function createComposerSuggestionComponent(composer, props) {
- const composerView = modelManager.messaging.models['ComposerView'].create({
+ const composerView = env.services.messaging.modelManager.messaging.models['ComposerView'].create({
qunitTest: insertAndReplace({
composer: replace(composer),
}),
@@ -434,9 +434,9 @@ function getCreateComposerSuggestionComponent({ env, modelManager, target }) {
};
}
-function getCreateMessageComponent({ env, modelManager, target }) {
+function getCreateMessageComponent({ env, target }) {
return async function createMessageComponent(message) {
- const messageView = modelManager.messaging.models['MessageView'].create({
+ const messageView = env.services.messaging.modelManager.messaging.models['MessageView'].create({
message: replace(message),
qunitTest: insertAndReplace(),
});
@@ -453,9 +453,9 @@ function getCreateMessagingMenuComponent({ env, target }) {
};
}
-function getCreateNotificationListComponent({ env, modelManager, target }) {
+function getCreateNotificationListComponent({ env, target }) {
return async function createNotificationListComponent({ filter = 'all' } = {}) {
- const notificationListView = modelManager.messaging.models['NotificationListView'].create({
+ const notificationListView = env.services.messaging.modelManager.messaging.models['NotificationListView'].create({
filter,
qunitTestOwner: insertAndReplace(),
});
@@ -595,10 +595,10 @@ async function start(param0 = {}) {
param0.serverData.views = { ...pyEnv.getViews(), ...param0.serverData.views };
const webClient = await getWebClientReady({ ...param0, messagingBus, testSetupDoneDeferred });
- const { modelManager } = webClient.env.services.messaging;
+ webClient.env.services.messaging.modelManager;
registerCleanup(async () => {
- await modelManager.messagingInitializedPromise;
- modelManager.messaging.delete();
+ await webClient.env.services.messaging.modelManager.messagingInitializedPromise;
+ webClient.env.services.messaging.modelManager.destroy();
webClient.env.services.legacy_bus_service.destroy();
delete webClient.env.services.messaging;
delete owl.Component.env.services.messaging;
@@ -606,11 +606,11 @@ async function start(param0 = {}) {
delete owl.Component.env;
});
if (waitUntilMessagingCondition === 'created') {
- await modelManager.messagingCreatedPromise;
+ await webClient.env.services.messaging.modelManager.messagingCreatedPromise;
}
if (waitUntilMessagingCondition === 'initialized') {
- await modelManager.messagingCreatedPromise;
- await modelManager.messagingInitializedPromise;
+ await webClient.env.services.messaging.modelManager.messagingCreatedPromise;
+ await webClient.env.services.messaging.modelManager.messagingInitializedPromise;
}
const openDiscuss = getOpenDiscuss(webClient, discuss);
if (autoOpenDiscuss) {
@@ -629,16 +629,16 @@ async function start(param0 = {}) {
afterNextRender,
click: getClick({ afterNextRender }),
createChatterContainerComponent: getCreateChatterContainerComponent({ afterEvent, env: webClient.env, target }),
- createComposerComponent: getCreateComposerComponent({ env: webClient.env, modelManager, target }),
- createComposerSuggestionComponent: getCreateComposerSuggestionComponent({ env: webClient.env, modelManager, target }),
- createMessageComponent: getCreateMessageComponent({ env: webClient.env, modelManager, target }),
+ createComposerComponent: getCreateComposerComponent({ env: webClient.env, target }),
+ createComposerSuggestionComponent: getCreateComposerSuggestionComponent({ env: webClient.env, target }),
+ createMessageComponent: getCreateMessageComponent({ env: webClient.env, target }),
createMessagingMenuComponent: getCreateMessagingMenuComponent({ env: webClient.env, target }),
- createNotificationListComponent: getCreateNotificationListComponent({ env: webClient.env, modelManager, target }),
+ createNotificationListComponent: getCreateNotificationListComponent({ env: webClient.env, target }),
createRootMessagingComponent: (componentName, props) => createRootMessagingComponent(webClient.env, componentName, { props, target }),
createThreadViewComponent: getCreateThreadViewComponent({ afterEvent, env: webClient.env, target }),
env: webClient.env,
insertText,
- messaging: modelManager.messaging,
+ messaging: webClient.env.services.messaging.modelManager.messaging,
openDiscuss,
openView,
pyEnv,