[IMP] web: support explicit mobile view mode on window actions

Up until this commit, which view was displayed by default on mobile was
somewhat of a lottery.

Initially, only kanban views were considered as 'more adapted to mobile
devices' and used as a main fallback to display records who had a kanban
view in the window action definition.

The the concept of 'mobile-friendly view' was extended to map and grid
so that these views could take precedence over the kanban view in
specific circumstances (indsutry_fsm and timesheet_grid, both of which
are enterprise edition apps) - basically they were marked as
mobile-friendly not because they *are*, but because it was the only way
to override the kanban override.

But this comes with its lot of problems and limitations, namely that the
mobile friendly view will be found based on the order of view modes for
a window action, so if you have a window action with the view modes
`kanban,map,tree,form`, you will never be able to have e.g. the kanban
view shown on desktop by default and the map view on mobile.

This commit introduces the notion of a 'default mobile view' on window
actions so that one may decide on an *arbitrary* view mode to use on
mobile for an action - without impacting the ordering of views on other
models, etc.

Task-3460374

closes odoo/odoo#133608

Related: odoo/enterprise#46562
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Damien Bouvy
2023-09-22 15:06:00 +00:00
committed by Aaron Bohy
parent 7e012dd544
commit 264ec1c4dd
6 changed files with 80 additions and 14 deletions
@@ -12,7 +12,6 @@ export const kanbanView = {
display_name: "Kanban",
icon: "oi oi-view-kanban",
isMobileFriendly: true,
multiRecord: true,
ArchParser: KanbanArchParser,
@@ -943,11 +943,10 @@ function makeActionManager(env) {
}
if (env.isSmall) {
if (!view.isMobileFriendly) {
view = _findMobileView(views, view.multiRecord) || view;
}
if (lazyView && !lazyView.isMobileFriendly) {
lazyView = _findMobileView(views, lazyView.multiRecord) || lazyView;
view = _findView(views, view.multiRecord, action.mobile_view_mode) || view;
if (lazyView) {
lazyView =
_findView(views, lazyView.multiRecord, action.mobile_view_mode) || lazyView;
}
}
@@ -984,15 +983,14 @@ function makeActionManager(env) {
}
/**
* Helper function to find the first mobile-friendly view, if any.
*
* @private
* @param {Array} views an array of views
* @param {boolean} multiRecord true if we search for a multiRecord view
* @returns {Object|undefined} first mobile-friendly view found
* @param {string} viewType type of the view to search
* @returns {Object|undefined} the requested view if it could be found
*/
function _findMobileView(views, multiRecord) {
return views.find((view) => view.isMobileFriendly && view.multiRecord === multiRecord);
function _findView(views, multiRecord, viewType) {
return views.find((v) => v.type === viewType && v.multiRecord == multiRecord);
}
// ---------------------------------------------------------------------------
@@ -0,0 +1,67 @@
/** @odoo-module **/
import { getFixture } from "@web/../tests/helpers/utils";
import { setupViewRegistries } from "@web/../tests/views/helpers";
import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
let serverData, target;
QUnit.module("ActionManager", (hooks) => {
hooks.beforeEach(() => {
serverData = {
models: {
project: {
fields: {
foo: { string: "Foo", type: "boolean" },
},
records: [
{
id: 1,
foo: true,
},
{
id: 2,
foo: false,
},
],
},
},
views: {
"project,false,list": '<list><field name="foo"/></list>',
"project,false,kanban": `
<kanban>
<templates>
<t t-name='kanban-box'>
<div class='oe_kanban_card'>
<field name='foo' />
</div>
</t>
</templates>
</kanban>
`,
"project,false,search": "<search></search>",
},
};
target = getFixture();
setupViewRegistries();
});
QUnit.module("Window Actions");
QUnit.test("execute a window action with mobile_view_mode", async (assert) => {
const webClient = await createWebClient({ serverData });
await doAction(webClient, {
xml_id: "project.action",
name: "Project Action",
res_model: "project",
type: "ir.actions.act_window",
view_mode: "list,kanban",
mobile_view_mode: "list",
views: [
[false, "kanban"],
[false, "list"],
],
});
assert.containsOnce(target, ".o_list_view");
});
});
@@ -174,6 +174,7 @@ export function getActionManagerServerData() {
xml_id: "action_3",
name: "Partners",
res_model: "partner",
mobile_view_mode: "kanban",
type: "ir.actions.act_window",
views: [
[false, "list"],
+3 -3
View File
@@ -272,6 +272,7 @@ class IrActionsActWindow(models.Model):
target = fields.Selection([('current', 'Current Window'), ('new', 'New Window'), ('inline', 'Inline Edit'), ('fullscreen', 'Full Screen'), ('main', 'Main action of Current Window')], default="current", string='Target Window')
view_mode = fields.Char(required=True, default='tree,form',
help="Comma-separated list of allowed view modes, such as 'form', 'tree', 'calendar', etc. (Default: tree,form)")
mobile_view_mode = fields.Char(default="kanban", help="First view mode in mobile and small screen environments (default='kanban'). If it can't be found among available view modes, the same mode as for wider screens is used)")
usage = fields.Char(string='Action Usage',
help="Used to filter menu and home actions from the user form.")
view_ids = fields.One2many('ir.actions.act_window.view', 'act_window_id', string='No of Views')
@@ -327,9 +328,8 @@ class IrActionsActWindow(models.Model):
def _get_readable_fields(self):
return super()._get_readable_fields() | {
"context", "domain", "filter", "groups_id", "limit", "res_id",
"res_model", "search_view_id", "target", "view_id",
"view_mode", "views",
"context", "mobile_view_mode", "domain", "filter", "groups_id", "limit",
"res_id", "res_model", "search_view_id", "target", "view_id", "view_mode", "views",
# `flags` is not a real field of ir.actions.act_window but is used
# to give the parameters to generate the action
"flags"
@@ -215,6 +215,7 @@
<group>
<group name="views" string="Views">
<field name="view_mode"/>
<field name="mobile_view_mode"/>
<field name="view_id"/>
<field name="search_view_id"/>
</group>