From 264ec1c4ddc9b8170ceb23fd7798df13f1180fe3 Mon Sep 17 00:00:00 2001 From: Damien Bouvy Date: Wed, 9 Aug 2023 14:50:48 +0200 Subject: [PATCH] [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) --- .../static/src/views/kanban/kanban_view.js | 1 - .../src/webclient/actions/action_service.js | 18 +++-- .../mobile/webclient/window_action_tests.js | 67 +++++++++++++++++++ addons/web/static/tests/webclient/helpers.js | 1 + odoo/addons/base/models/ir_actions.py | 6 +- odoo/addons/base/views/ir_actions_views.xml | 1 + 6 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 addons/web/static/tests/mobile/webclient/window_action_tests.js diff --git a/addons/web/static/src/views/kanban/kanban_view.js b/addons/web/static/src/views/kanban/kanban_view.js index 0cb59157b78..a82f5607218 100644 --- a/addons/web/static/src/views/kanban/kanban_view.js +++ b/addons/web/static/src/views/kanban/kanban_view.js @@ -12,7 +12,6 @@ export const kanbanView = { display_name: "Kanban", icon: "oi oi-view-kanban", - isMobileFriendly: true, multiRecord: true, ArchParser: KanbanArchParser, diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js index 2ae1993c2b2..de0dff5eaa3 100644 --- a/addons/web/static/src/webclient/actions/action_service.js +++ b/addons/web/static/src/webclient/actions/action_service.js @@ -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); } // --------------------------------------------------------------------------- diff --git a/addons/web/static/tests/mobile/webclient/window_action_tests.js b/addons/web/static/tests/mobile/webclient/window_action_tests.js new file mode 100644 index 00000000000..d809e5c3194 --- /dev/null +++ b/addons/web/static/tests/mobile/webclient/window_action_tests.js @@ -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": '', + "project,false,kanban": ` + + + +
+ +
+
+
+
+ `, + "project,false,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"); + }); +}); diff --git a/addons/web/static/tests/webclient/helpers.js b/addons/web/static/tests/webclient/helpers.js index 5ebcc47a6bf..b93f8889a7d 100644 --- a/addons/web/static/tests/webclient/helpers.js +++ b/addons/web/static/tests/webclient/helpers.js @@ -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"], diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py index 388b8fa5d42..bbe6cec211a 100644 --- a/odoo/addons/base/models/ir_actions.py +++ b/odoo/addons/base/models/ir_actions.py @@ -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" diff --git a/odoo/addons/base/views/ir_actions_views.xml b/odoo/addons/base/views/ir_actions_views.xml index a92e825344d..eca3223fda1 100644 --- a/odoo/addons/base/views/ir_actions_views.xml +++ b/odoo/addons/base/views/ir_actions_views.xml @@ -215,6 +215,7 @@ +