[IMP] mail: clean dialog modelling of discuss
Properly define relational fields as owner/owning/content. For instance, with viewing attachment in a dialog, the modelling looked roughly like this before the commit: ``` attachment_viewer -----> dialog ``` - where attachment viewer owns dialog This commit changes it to: ``` attachment (image/card) ------> dialog -------> attachment_viewer ``` - where attachment (image/card) owns the dialog, and dialog has attachment viewer as content These changes should make modelling of dialog feels more natural. Task-2738648 closes odoo/odoo#83519 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -114,7 +114,7 @@ export class AttachmentViewer extends Component {
|
||||
* @private
|
||||
*/
|
||||
_close() {
|
||||
this.attachmentViewer.close();
|
||||
this.attachmentViewer.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,14 +163,10 @@ export class AttachmentViewer extends Component {
|
||||
* @private
|
||||
*/
|
||||
_next() {
|
||||
const attachmentViewer = this.attachmentViewer;
|
||||
const index = attachmentViewer.attachments.findIndex(attachment =>
|
||||
attachment === attachmentViewer.attachment
|
||||
);
|
||||
const nextIndex = (index + 1) % attachmentViewer.attachments.length;
|
||||
attachmentViewer.update({
|
||||
attachment: link(attachmentViewer.attachments[nextIndex]),
|
||||
});
|
||||
if (!this.attachmentViewer.dialogOwner || !this.attachmentViewer.dialogOwner.attachmentListOwnerAsAttachmentView) {
|
||||
return;
|
||||
}
|
||||
this.attachmentViewer.dialogOwner.attachmentListOwnerAsAttachmentView.selectNextAttachment();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,16 +175,10 @@ export class AttachmentViewer extends Component {
|
||||
* @private
|
||||
*/
|
||||
_previous() {
|
||||
const attachmentViewer = this.attachmentViewer;
|
||||
const index = attachmentViewer.attachments.findIndex(attachment =>
|
||||
attachment === attachmentViewer.attachment
|
||||
);
|
||||
const nextIndex = index === 0
|
||||
? attachmentViewer.attachments.length - 1
|
||||
: index - 1;
|
||||
attachmentViewer.update({
|
||||
attachment: link(attachmentViewer.attachments[nextIndex]),
|
||||
});
|
||||
if (!this.attachmentViewer.dialogOwner || !this.attachmentViewer.dialogOwner.attachmentListOwnerAsAttachmentView) {
|
||||
return;
|
||||
}
|
||||
this.attachmentViewer.dialogOwner.attachmentListOwnerAsAttachmentView.selectPreviousAttachment();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { insert, insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
import { insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentCard',
|
||||
@@ -15,13 +15,9 @@ registerModel({
|
||||
if (!this.attachment || !this.attachment.isViewable) {
|
||||
return;
|
||||
}
|
||||
this.messaging.dialogManager.update({
|
||||
dialogs: insert({
|
||||
attachmentViewer: insertAndReplace({
|
||||
attachment: replace(this.attachment),
|
||||
attachmentList: replace(this.attachmentList),
|
||||
}),
|
||||
}),
|
||||
this.attachmentList.update({
|
||||
attachmentListViewDialog: insertAndReplace(),
|
||||
selectedAttachment: replace(this.attachment),
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear, insert, insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
import { clear, insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentImage',
|
||||
@@ -15,13 +15,9 @@ registerModel({
|
||||
if (!this.attachment || !this.attachment.isViewable) {
|
||||
return;
|
||||
}
|
||||
this.messaging.dialogManager.update({
|
||||
dialogs: insert({
|
||||
attachmentViewer: insertAndReplace({
|
||||
attachment: replace(this.attachment),
|
||||
attachmentList: replace(this.attachmentList),
|
||||
}),
|
||||
}),
|
||||
this.attachmentList.update({
|
||||
attachmentListViewDialog: insertAndReplace(),
|
||||
selectedAttachment: replace(this.attachment),
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,22 @@ registerModel({
|
||||
name: 'AttachmentList',
|
||||
identifyingFields: [['composerView', 'messageView', 'attachmentBoxViewOwner']],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Select the next attachment.
|
||||
*/
|
||||
selectNextAttachment() {
|
||||
const index = this.attachments.findIndex(attachment => attachment === this.selectedAttachment);
|
||||
const nextIndex = index === this.attachments.length - 1 ? 0 : index + 1;
|
||||
this.update({ selectedAttachment: replace(this.attachments[nextIndex]) });
|
||||
},
|
||||
/**
|
||||
* Select the previous attachment.
|
||||
*/
|
||||
selectPreviousAttachment() {
|
||||
const index = this.attachments.findIndex(attachment => attachment === this.selectedAttachment);
|
||||
const prevIndex = index === 0 ? this.attachments.length - 1 : index - 1;
|
||||
this.update({ selectedAttachment: replace(this.attachments[prevIndex]) });
|
||||
},
|
||||
_computeAttachmentImages() {
|
||||
return insertAndReplace(this.imageAttachments.map(attachment => {
|
||||
return {
|
||||
@@ -80,6 +96,10 @@ registerModel({
|
||||
inverse: 'attachmentList',
|
||||
isCausal: true,
|
||||
}),
|
||||
attachmentListViewDialog: one('Dialog', {
|
||||
inverse: 'attachmentListOwnerAsAttachmentView',
|
||||
isCausal: true,
|
||||
}),
|
||||
/**
|
||||
* States the attachments to be displayed by this attachment list.
|
||||
*/
|
||||
@@ -116,6 +136,7 @@ registerModel({
|
||||
nonImageAttachments: many('Attachment', {
|
||||
compute: '_computeNonImageAttachments',
|
||||
}),
|
||||
selectedAttachment: one('Attachment'),
|
||||
/**
|
||||
* States the attachments that can be viewed inside the browser.
|
||||
*/
|
||||
|
||||
@@ -5,17 +5,8 @@ import { attr, many, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentViewer',
|
||||
identifyingFields: ['attachmentList'],
|
||||
identifyingFields: ['dialogOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Close the attachment viewer by closing its linked dialog.
|
||||
*/
|
||||
close() {
|
||||
const dialog = this.messaging.models['Dialog'].find(dialog => dialog.record === this);
|
||||
if (dialog) {
|
||||
dialog.delete();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns whether the given html element is inside this attachment viewer.
|
||||
*
|
||||
@@ -47,9 +38,11 @@ registerModel({
|
||||
angle: attr({
|
||||
default: 0,
|
||||
}),
|
||||
attachment: one('Attachment'),
|
||||
attachment: one('Attachment', {
|
||||
related: 'attachmentList.selectedAttachment',
|
||||
}),
|
||||
attachmentList: one('AttachmentList', {
|
||||
readonly: true,
|
||||
related: 'dialogOwner.attachmentListOwnerAsAttachmentView',
|
||||
required: true,
|
||||
}),
|
||||
attachments: many('Attachment', {
|
||||
@@ -62,7 +55,7 @@ registerModel({
|
||||
/**
|
||||
* Determines the dialog displaying this attachment viewer.
|
||||
*/
|
||||
dialog: one('Dialog', {
|
||||
dialogOwner: one('Dialog', {
|
||||
inverse: 'attachmentViewer',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear, replace } from '@mail/model/model_field_command';
|
||||
import { clear, insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Dialog',
|
||||
identifyingFields: ['manager', ['attachmentViewer', 'followerSubtypeList']],
|
||||
identifyingFields: [[
|
||||
'attachmentListOwnerAsAttachmentView',
|
||||
'followerOwnerAsSubtypeList',
|
||||
]],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {Element} element
|
||||
@@ -15,6 +18,40 @@ registerModel({
|
||||
hasElementInContent(element) {
|
||||
return Boolean(this.record && this.record.containsElement(element));
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeAttachmentViewer() {
|
||||
if (this.attachmentListOwnerAsAttachmentView) {
|
||||
return insertAndReplace();
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeComponentName() {
|
||||
if (this.attachmentViewer) {
|
||||
return 'AttachmentViewer';
|
||||
}
|
||||
if (this.followerSubtypeList) {
|
||||
return 'FollowerSubtypeList';
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeFollowerSubtypeList() {
|
||||
if (this.followerOwnerAsSubtypeList) {
|
||||
return insertAndReplace();
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
@@ -29,6 +66,16 @@ registerModel({
|
||||
}
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeManager() {
|
||||
if (this.messaging.dialogManager) {
|
||||
return replace(this.messaging.dialogManager);
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
@@ -41,36 +88,36 @@ registerModel({
|
||||
return replace(this.followerSubtypeList);
|
||||
}
|
||||
},
|
||||
_computeComponentName() {
|
||||
if (this.attachmentViewer) {
|
||||
return 'AttachmentViewer';
|
||||
}
|
||||
if (this.followerSubtypeList) {
|
||||
return 'FollowerSubtypeList';
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
attachmentViewer: one('AttachmentViewer', {
|
||||
isCausal: true,
|
||||
inverse: 'dialog',
|
||||
attachmentListOwnerAsAttachmentView: one('AttachmentList', {
|
||||
inverse: 'attachmentListViewDialog',
|
||||
readonly: true,
|
||||
}),
|
||||
attachmentViewer: one('AttachmentViewer', {
|
||||
compute: '_computeAttachmentViewer',
|
||||
inverse: 'dialogOwner',
|
||||
isCausal: true,
|
||||
}),
|
||||
componentName: attr({
|
||||
compute: '_computeComponentName',
|
||||
required: true,
|
||||
}),
|
||||
followerSubtypeList: one('FollowerSubtypeList', {
|
||||
isCausal: true,
|
||||
inverse: 'dialog',
|
||||
followerOwnerAsSubtypeList: one('Follower', {
|
||||
inverse: 'followerSubtypeListDialog',
|
||||
readonly: true,
|
||||
}),
|
||||
followerSubtypeList: one('FollowerSubtypeList', {
|
||||
compute: '_computeFollowerSubtypeList',
|
||||
inverse: 'dialogOwner',
|
||||
isCausal: true,
|
||||
}),
|
||||
isCloseable: attr({
|
||||
compute: '_computeIsCloseable',
|
||||
default: true,
|
||||
}),
|
||||
manager: one('DialogManager', {
|
||||
compute: '_computeManager',
|
||||
inverse: 'dialogs',
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear, insert, insertAndReplace, link, replace, unlink, unlinkAll } from '@mail/model/model_field_command';
|
||||
import { clear, insert, insertAndReplace, link, unlink, unlinkAll } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'Follower',
|
||||
@@ -47,7 +47,7 @@ registerModel({
|
||||
* Close subtypes dialog
|
||||
*/
|
||||
closeSubtypes() {
|
||||
this.update({ subtypeList: clear() });
|
||||
this.update({ followerSubtypeListDialog: clear() });
|
||||
},
|
||||
/**
|
||||
* Opens the most appropriate view that is a profile for this follower.
|
||||
@@ -98,13 +98,7 @@ registerModel({
|
||||
this.update({ selectedSubtypes: unlink(subtype) });
|
||||
}
|
||||
}
|
||||
this.messaging.dialogManager.update({
|
||||
dialogs: insert({
|
||||
followerSubtypeList: insertAndReplace({
|
||||
follower: replace(this),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
this.update({ followerSubtypeListDialog: insertAndReplace() });
|
||||
},
|
||||
/**
|
||||
* @param {FollowerSubtype} subtype
|
||||
@@ -145,6 +139,10 @@ registerModel({
|
||||
followedThread: one('Thread', {
|
||||
inverse: 'followers',
|
||||
}),
|
||||
followerSubtypeListDialog: one('Dialog', {
|
||||
inverse: 'followerOwnerAsSubtypeList',
|
||||
isCausal: true,
|
||||
}),
|
||||
id: attr({
|
||||
readonly: true,
|
||||
required: true,
|
||||
@@ -159,10 +157,6 @@ registerModel({
|
||||
required: true,
|
||||
}),
|
||||
selectedSubtypes: many('FollowerSubtype'),
|
||||
subtypeList: many('FollowerSubtypeList', {
|
||||
inverse: 'follower',
|
||||
isCausal: true,
|
||||
}),
|
||||
subtypes: many('FollowerSubtype'),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'FollowerSubtypeList',
|
||||
identifyingFields: ['follower'],
|
||||
identifyingFields: ['dialogOwner'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Returns whether the given html element is inside this follower subtype list.
|
||||
@@ -25,14 +26,13 @@ registerModel({
|
||||
/**
|
||||
* States the dialog displaying this follower subtype list.
|
||||
*/
|
||||
dialog: one('Dialog', {
|
||||
dialogOwner: one('Dialog', {
|
||||
inverse: 'followerSubtypeList',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
follower: one('Follower', {
|
||||
inverse: 'subtypeList',
|
||||
readonly: true,
|
||||
related: 'dialogOwner.followerOwnerAsSubtypeList',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user