[FIX] web: kanban view's quick create flickering
Before this commit, when creating a new record using the quick create on a kanban view, a flickering was visible. We can see the quick create form disappear, the record list goes up, the quick create form re-appear and the record list goes down again. This occurs because, the record list contains an empty record that is used on the quick create form. When clicking on the add button on the quick create form, this record is directly saved, and as is already on the list (the quick create form disappear and the record list goes up), only after this action, a new empty record is created and added to the list (the quick create form re-appear and the record list goes down again). Now, an empty record is created independently of the list, so when we click on the add button on the quick create form, the record is saved, a new empty record is created (the quick create form is emptied), and the saved record is added to the list (the record list got the new saved record), avoiding the flickering. task-id=3085247 closes odoo/odoo#112367 X-original-commit: 9c82f5dca00921b6911671db3845ad593d2e1191 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com> Signed-off-by: Jorge Pinna Puissant (jpp) <jpp@odoo.com>
This commit is contained in:
@@ -287,24 +287,30 @@ class KanbanGroup extends Group {
|
||||
* @param {number} index
|
||||
* @returns {Promise<Record | false>}
|
||||
*/
|
||||
async validateQuickCreate() {
|
||||
const record = this.list.quickCreateRecord;
|
||||
async validateQuickCreate(record, mode) {
|
||||
let saved = false;
|
||||
if (record) {
|
||||
saved = await this.model.mutex.exec(async () => {
|
||||
const saved = await record._save({ noReload: true, stayInEdition: true });
|
||||
if (saved) {
|
||||
this.count++;
|
||||
if (record.parentActiveFields) {
|
||||
record.setActiveFields(record.parentActiveFields);
|
||||
record.parentActiveFields = false;
|
||||
}
|
||||
await this.model.reloadRecords(record);
|
||||
record.switchMode("readonly");
|
||||
this.addRecord(this.removeRecord(record), 0);
|
||||
}
|
||||
return saved;
|
||||
return await record._save({ noReload: true, stayInEdition: true });
|
||||
});
|
||||
if (saved) {
|
||||
if (mode === "add") {
|
||||
await this.model.root.quickCreate(this);
|
||||
} else {
|
||||
this.quickCreateRecord = null;
|
||||
}
|
||||
if (record.parentActiveFields) {
|
||||
record.setActiveFields(record.parentActiveFields);
|
||||
record.parentActiveFields = false;
|
||||
}
|
||||
await this.model.reloadRecords(record);
|
||||
record.switchMode("readonly");
|
||||
this.addRecord(record, 0);
|
||||
this.model.trigger("group-updated", {
|
||||
group: this,
|
||||
withProgressBars: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
return saved ? record : false;
|
||||
}
|
||||
@@ -605,9 +611,7 @@ export class KanbanModel extends RelationalModel {
|
||||
// example background. Return true so that we don't get sample data instead
|
||||
return true;
|
||||
}
|
||||
return this.root.groups.some(
|
||||
(group) => group.count > 0 || group.list.quickCreateRecord
|
||||
);
|
||||
return this.root.groups.some((group) => group.count > 0 || group.quickCreateRecord);
|
||||
}
|
||||
return this.root.records.length > 0;
|
||||
}
|
||||
|
||||
@@ -378,10 +378,10 @@ export class KanbanRenderer extends Component {
|
||||
}
|
||||
|
||||
async validateQuickCreate(mode, group) {
|
||||
const values = group.list.quickCreateRecord.data;
|
||||
let record;
|
||||
const values = group.quickCreateRecord.data;
|
||||
let record = group.quickCreateRecord;
|
||||
try {
|
||||
record = await group.validateQuickCreate();
|
||||
record = await group.validateQuickCreate(record, mode);
|
||||
} catch (e) {
|
||||
// TODO: filter RPC errors more specifically (eg, for access denied, there is no point in opening a dialog)
|
||||
if (!(e instanceof RPCError)) {
|
||||
@@ -410,12 +410,8 @@ export class KanbanRenderer extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (record) {
|
||||
if (mode === "edit") {
|
||||
await this.props.openRecord(record, "edit");
|
||||
} else {
|
||||
await this.quickCreate(group);
|
||||
}
|
||||
if (mode === "edit" && record) {
|
||||
await this.props.openRecord(record, "edit");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,30 +60,28 @@
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="!group.isFolded">
|
||||
<t t-if="props.list.quickCreateInfo and group.quickCreateRecord">
|
||||
<KanbanRecordQuickCreate
|
||||
record="group.quickCreateRecord"
|
||||
archInfo="props.list.quickCreateInfo"
|
||||
onCancel="force => group.cancelQuickCreate(force)"
|
||||
onValidate="mode => this.validateQuickCreate(mode, group)"
|
||||
listIsGrouped="props.list.isGrouped"
|
||||
/>
|
||||
</t>
|
||||
<t t-foreach="group.list.records" t-as="record" t-key="record.id">
|
||||
<t t-if="record.isInQuickCreation and props.list.quickCreateInfo">
|
||||
<KanbanRecordQuickCreate
|
||||
record="record"
|
||||
archInfo="props.list.quickCreateInfo"
|
||||
onCancel="force => group.list.cancelQuickCreate(force)"
|
||||
onValidate="mode => this.validateQuickCreate(mode, group)"
|
||||
listIsGrouped="props.list.isGrouped"
|
||||
/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<KanbanRecord
|
||||
archInfo="props.archInfo"
|
||||
Compiler="props.Compiler"
|
||||
canResequence="canResequenceRecords"
|
||||
forceGlobalClick="props.forceGlobalClick"
|
||||
group="group"
|
||||
list="props.list"
|
||||
openRecord="props.openRecord"
|
||||
readonly="props.readonly"
|
||||
record="record"
|
||||
templates="props.archInfo.templateDocs"
|
||||
/>
|
||||
</t>
|
||||
<KanbanRecord
|
||||
archInfo="props.archInfo"
|
||||
Compiler="props.Compiler"
|
||||
canResequence="canResequenceRecords"
|
||||
forceGlobalClick="props.forceGlobalClick"
|
||||
group="group"
|
||||
list="props.list"
|
||||
openRecord="props.openRecord"
|
||||
readonly="props.readonly"
|
||||
record="record"
|
||||
templates="props.archInfo.templateDocs"
|
||||
/>
|
||||
</t>
|
||||
<t t-set="unloadedCount" t-value="getGroupUnloadedCount(group)" />
|
||||
<div t-if="unloadedCount > 0" class="o_kanban_load_more">
|
||||
|
||||
@@ -1893,10 +1893,6 @@ export class DynamicRecordList extends DynamicList {
|
||||
};
|
||||
}
|
||||
|
||||
get quickCreateRecord() {
|
||||
return this.records.find((r) => r.isInQuickCreation);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1931,13 +1927,6 @@ export class DynamicRecordList extends DynamicList {
|
||||
return record;
|
||||
}
|
||||
|
||||
async cancelQuickCreate(force = false) {
|
||||
const record = this.quickCreateRecord;
|
||||
if (record && (force || !record.isDirty)) {
|
||||
this.removeRecord(record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} [params={}]
|
||||
* @param {boolean} [atFirstPosition]
|
||||
@@ -1956,8 +1945,11 @@ export class DynamicRecordList extends DynamicList {
|
||||
await this.model.keepLast.add(this.model.mutex.exec(() => newRecord.load()));
|
||||
this.editedRecord = newRecord;
|
||||
this.onRemoveNewRecord = await this.onCreateRecord(newRecord);
|
||||
|
||||
return this.addRecord(newRecord, atFirstPosition ? 0 : this.count);
|
||||
if (params.isInQuickCreation) {
|
||||
return newRecord;
|
||||
} else {
|
||||
return this.addRecord(newRecord, atFirstPosition ? 0 : this.count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2039,10 +2031,6 @@ export class DynamicRecordList extends DynamicList {
|
||||
|
||||
async quickCreate(activeFields, context) {
|
||||
await this.model.mutex.getUnlockedDef();
|
||||
const record = this.quickCreateRecord;
|
||||
if (record) {
|
||||
this.removeRecord(record);
|
||||
}
|
||||
const rawContext = {
|
||||
parent: this.rawContext,
|
||||
make: () => makeContext([context, {}]),
|
||||
@@ -2352,7 +2340,11 @@ export class DynamicGroupList extends DynamicList {
|
||||
if (isFolded) {
|
||||
await group.toggle();
|
||||
}
|
||||
await group.quickCreate(this.quickCreateInfo.activeFields, this.context);
|
||||
group.quickCreateRecord = await group.quickCreate(
|
||||
this.quickCreateInfo.activeFields,
|
||||
this.context
|
||||
);
|
||||
this.model.notify();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2788,6 +2780,13 @@ export class Group extends DataPoint {
|
||||
return this.list.quickCreate(activeFields, ctx);
|
||||
}
|
||||
|
||||
async cancelQuickCreate(force = false) {
|
||||
if (this.quickCreateRecord && (force || !this.quickCreateRecord.isDirty)) {
|
||||
this.quickCreateRecord = null;
|
||||
this.model.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DynamicRecordList.removeRecord
|
||||
*/
|
||||
|
||||
@@ -1544,8 +1544,8 @@ QUnit.module("Views", (hooks) => {
|
||||
"web_search_read", // initial search_read (second column)
|
||||
"onchange", // quick create
|
||||
"name_create", // should perform a name_create to create the record
|
||||
"read", // read the created record
|
||||
"onchange", // reopen the quick create automatically
|
||||
"read", // read the created record
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1621,11 +1621,76 @@ QUnit.module("Views", (hooks) => {
|
||||
"get_views", // form view in quick create
|
||||
"onchange", // quick create
|
||||
"create", // should perform a create to create the record
|
||||
"read", // read the created record
|
||||
"onchange", // new quick create
|
||||
"read", // read the created record
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("quick create record flickering", async (assert) => {
|
||||
let def;
|
||||
serverData.views["partner,some_view_ref,form"] =
|
||||
"<form>" +
|
||||
'<field name="foo"/>' +
|
||||
'<field name="int_field"/>' +
|
||||
'<field name="state" widget="priority"/>' +
|
||||
"</form>";
|
||||
|
||||
await makeView({
|
||||
type: "kanban",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch:
|
||||
'<kanban on_create="quick_create" quick_create_view="some_view_ref">' +
|
||||
'<field name="bar"/>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div><field name="foo"/></div>' +
|
||||
"</t></templates></kanban>",
|
||||
groupBy: ["bar"],
|
||||
async mockRPC(route, args) {
|
||||
if (args.method === "create") {
|
||||
assert.deepEqual(
|
||||
args.args[0],
|
||||
{
|
||||
foo: "new partner",
|
||||
int_field: 4,
|
||||
state: "def",
|
||||
},
|
||||
"should send the correct values"
|
||||
);
|
||||
}
|
||||
if (args.method === "onchange") {
|
||||
await def;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// click on 'Create' -> should open the quick create in the first column
|
||||
await createRecord();
|
||||
|
||||
assert.containsN(target, ".o_kanban_group:first-child .o_kanban_record", 1);
|
||||
assert.containsOnce(target, ".o_kanban_group:first-child .o_kanban_quick_create");
|
||||
const quickCreate = target.querySelector(
|
||||
".o_kanban_group:first-child .o_kanban_quick_create"
|
||||
);
|
||||
|
||||
assert.containsOnce(quickCreate, ".o_form_view.o_xxs_form_view");
|
||||
assert.containsN(quickCreate, "input", 2);
|
||||
assert.containsN(quickCreate, ".o_field_widget", 3, "should have rendered three widgets");
|
||||
|
||||
// fill the quick create and validate
|
||||
await editQuickCreateInput("foo", "new partner");
|
||||
await editQuickCreateInput("int_field", 4);
|
||||
await click(quickCreate, ".o_field_widget[name=state] .o_priority_star:first-child");
|
||||
def = makeDeferred();
|
||||
await validateRecord();
|
||||
assert.containsN(target, ".o_kanban_group:first-child .o_kanban_record", 1);
|
||||
assert.containsOnce(target, ".o_kanban_group:first-child .o_kanban_quick_create");
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.containsN(target, ".o_kanban_group:first-child .o_kanban_record", 2);
|
||||
assert.containsOnce(target, ".o_kanban_group:first-child .o_kanban_quick_create");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"quick create record should focus default field [REQUIRE FOCUS]",
|
||||
async function (assert) {
|
||||
@@ -1779,8 +1844,8 @@ QUnit.module("Views", (hooks) => {
|
||||
"web_search_read", // initial search_read (second column)
|
||||
"onchange", // quick create
|
||||
"name_create", // should perform a name_create to create the record
|
||||
"read", // read the created record
|
||||
"onchange", // reopen the quick create automatically
|
||||
"read", // read the created record
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1847,8 +1912,8 @@ QUnit.module("Views", (hooks) => {
|
||||
"get_views", // form view in quick create
|
||||
"onchange", // quick create
|
||||
"create", // should perform a create to create the record
|
||||
"read", // read the created record
|
||||
"onchange", // reopen the quick create automatically
|
||||
"read", // read the created record
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1903,9 +1968,9 @@ QUnit.module("Views", (hooks) => {
|
||||
"read", // read display_name of categories
|
||||
"onchange", // quick create
|
||||
"name_create", // should perform a name_create to create the record
|
||||
"onchange", // reopen the quick create automatically
|
||||
"read",
|
||||
"read", // read the created record
|
||||
"onchange", // reopen the quick create automatically
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1963,8 +2028,8 @@ QUnit.module("Views", (hooks) => {
|
||||
"web_search_read", // read records when unfolding 'None'
|
||||
"onchange", // quick create
|
||||
"name_create", // should perform a name_create to create the record
|
||||
"read", // read the created record
|
||||
"onchange", // reopen the quick create automatically
|
||||
"read", // read the created record
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -2025,8 +2090,8 @@ QUnit.module("Views", (hooks) => {
|
||||
"get_views", // get form view
|
||||
"onchange", // quick create
|
||||
"create", // should perform a name_create to create the record
|
||||
"read", // read the created record
|
||||
"onchange", // reopen the quick create automatically
|
||||
"read", // read the created record
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -2098,9 +2163,9 @@ QUnit.module("Views", (hooks) => {
|
||||
"onchange", // quick create
|
||||
"read",
|
||||
"create", // should perform a create to create the record
|
||||
"read",
|
||||
"onchange",
|
||||
"read",
|
||||
"read",
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -2295,7 +2360,7 @@ QUnit.module("Views", (hooks) => {
|
||||
await editQuickCreateInput("foo", "new partner");
|
||||
assert.verifySteps(["onchange"]);
|
||||
await validateRecord();
|
||||
assert.verifySteps(["create", "read", "onchange"]);
|
||||
assert.verifySteps(["create", "onchange", "read"]);
|
||||
});
|
||||
|
||||
QUnit.test("quick create record and change state in grouped mode", async (assert) => {
|
||||
|
||||
Reference in New Issue
Block a user