mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
move code in init file, remove menu helpers
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
import { Ajax } from "./core/ajax";
|
||||
import { NotificationManager } from "./core/notifications";
|
||||
import { QWeb } from "./core/qweb_vdom";
|
||||
import { Router } from "./core/router";
|
||||
import { idGenerator, memoize } from "./core/utils";
|
||||
import { actionRegistry } from "./registries";
|
||||
import { ActionManager } from "./services/action_manager";
|
||||
import { Env } from "./widgets/widget";
|
||||
|
||||
/**
|
||||
* makeEnvironment returns the main environment for the application.
|
||||
*
|
||||
* Note that it does not make much sense (except for tests) to have more than
|
||||
* one environment. For example, with two environment, the router code in one
|
||||
* environment will probably interfere with the code from the other environment.
|
||||
*
|
||||
* For this reason, the result of makeEnvironment is memoized: every call to
|
||||
* this function will actually return the same environment.
|
||||
*/
|
||||
export const makeEnvironment = memoize(async function(): Promise<Env> {
|
||||
// services
|
||||
const qweb = new QWeb();
|
||||
const router = new Router();
|
||||
const ajax = new Ajax();
|
||||
const actionManager = new ActionManager(actionRegistry);
|
||||
const notifications = new NotificationManager();
|
||||
|
||||
// templates
|
||||
const result = await fetch("templates.xml");
|
||||
if (!result.ok) {
|
||||
throw new Error("Error while fetching xml templates");
|
||||
}
|
||||
const templates = await result.text();
|
||||
qweb.addTemplate("default", "<div/>");
|
||||
qweb.loadTemplates(templates);
|
||||
|
||||
return {
|
||||
// Base widget requirements
|
||||
qweb,
|
||||
getID: idGenerator(),
|
||||
|
||||
actionManager,
|
||||
ajax,
|
||||
notifications,
|
||||
actionRegistry,
|
||||
router,
|
||||
|
||||
rpc: ajax.rpc,
|
||||
|
||||
debug: false,
|
||||
isMobile: window.innerWidth <= 768
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import { Ajax } from "./core/ajax";
|
||||
import { NotificationManager } from "./core/notifications";
|
||||
import { QWeb } from "./core/qweb_vdom";
|
||||
import { Router } from "./core/router";
|
||||
import { findInTree, idGenerator, memoize } from "./core/utils";
|
||||
import { actionRegistry } from "./registries";
|
||||
import { ActionManager } from "./services/action_manager";
|
||||
import { MenuInfo, MenuItem } from "./widgets/root";
|
||||
import { Env } from "./widgets/widget";
|
||||
|
||||
interface InitializedData {
|
||||
env: Env;
|
||||
menuInfo: MenuInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* makeEnvironment returns the main environment for the application.
|
||||
*
|
||||
* Note that it does not make much sense (except for tests) to have more than
|
||||
* one environment. For example, with two environment, the router code in one
|
||||
* environment will probably interfere with the code from the other environment.
|
||||
*
|
||||
* For this reason, the result of makeEnvironment is memoized: every call to
|
||||
* this function will actually return the same environment.
|
||||
*/
|
||||
export const init = memoize(async function(): Promise<InitializedData> {
|
||||
// services
|
||||
const qweb = new QWeb();
|
||||
const router = new Router();
|
||||
const ajax = new Ajax();
|
||||
const actionManager = new ActionManager(actionRegistry);
|
||||
const notifications = new NotificationManager();
|
||||
|
||||
// templates
|
||||
const templates = await loadTemplates();
|
||||
qweb.addTemplate("default", "<div/>");
|
||||
qweb.loadTemplates(templates);
|
||||
|
||||
const menuInfo = loadMenus();
|
||||
|
||||
const env: Env = {
|
||||
// Base widget requirements
|
||||
qweb,
|
||||
getID: idGenerator(),
|
||||
|
||||
actionManager,
|
||||
ajax,
|
||||
notifications,
|
||||
actionRegistry,
|
||||
router,
|
||||
|
||||
rpc: ajax.rpc,
|
||||
|
||||
debug: false,
|
||||
isMobile: window.innerWidth <= 768
|
||||
};
|
||||
return { env, menuInfo };
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Adapters
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function loadMenus(): MenuInfo {
|
||||
const menuItems: BaseMenuItem[] = (<any>window).odoo.menus;
|
||||
const menuInfo = getMenuInfo(menuItems);
|
||||
delete (<any>window).odoo.menus; // overkill?
|
||||
return menuInfo;
|
||||
}
|
||||
|
||||
async function loadTemplates(): Promise<string> {
|
||||
const result = await fetch("templates.xml");
|
||||
if (!result.ok) {
|
||||
throw new Error("Error while fetching xml templates");
|
||||
}
|
||||
return result.text();
|
||||
}
|
||||
|
||||
interface BaseMenuItem {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number | false;
|
||||
action: string | false;
|
||||
icon: string | false;
|
||||
children: BaseMenuItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a valid MenuInfo object from a list of BaseMenuItems. This function
|
||||
* is supposed to be called once at startup.
|
||||
*/
|
||||
export function getMenuInfo(items: BaseMenuItem[]): MenuInfo {
|
||||
const menuMap: { [key: number]: MenuItem | undefined } = {};
|
||||
const actionMap: { [id: number]: number | undefined } = {};
|
||||
const roots: number[] = [];
|
||||
|
||||
// build MenuItems
|
||||
for (let root of items) {
|
||||
roots.push(root.id);
|
||||
addToMap(root, root.id);
|
||||
}
|
||||
|
||||
function addToMap(m: BaseMenuItem, appId: number): MenuItem {
|
||||
const item: MenuItem = {
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
parentId: m.parent_id,
|
||||
action: m.action,
|
||||
icon: m.icon,
|
||||
appId: appId,
|
||||
actionId: -1, // will be filled later on with correct id
|
||||
children: m.children.map(c => addToMap(c, appId))
|
||||
};
|
||||
menuMap[item.id] = item;
|
||||
return item;
|
||||
}
|
||||
|
||||
// add proper actionId to every menuitems
|
||||
for (let menuId in menuMap) {
|
||||
const menu = menuMap[menuId]!;
|
||||
let menuWithAction = menu && findInTree(menu, m => Boolean(m.action));
|
||||
if (menuWithAction) {
|
||||
menu.actionId = parseInt(
|
||||
(<string>menuWithAction.action).split(",")[1],
|
||||
10
|
||||
);
|
||||
if (menuWithAction === menu) {
|
||||
actionMap[menu.actionId] = menu.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { menuMap, roots, actionMap };
|
||||
}
|
||||
@@ -1,23 +1,19 @@
|
||||
///<amd-module name="main" />
|
||||
|
||||
import { makeEnvironment } from "./env";
|
||||
import { BaseMenuItem, processMenuItems } from "./misc/menu_helpers";
|
||||
import { init } from "./init";
|
||||
import { Root } from "./widgets/root";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Application bootstrapping
|
||||
// Web Client Bootstrapping
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async function() {
|
||||
const env = await makeEnvironment();
|
||||
const { env, menuInfo } = await init();
|
||||
|
||||
// menu processing
|
||||
const menuItems: BaseMenuItem[] = (<any>window).odoo.menus;
|
||||
const menuInfo = processMenuItems(menuItems);
|
||||
delete (<any>window).odoo.menus; // overkill?
|
||||
|
||||
// creating root widget
|
||||
// Creating root widget
|
||||
const rootWidget = new Root(env, { menuInfo });
|
||||
await rootWidget.mount(document.body);
|
||||
|
||||
// For debugging purpose, we keep a reference to the root widget in odoo
|
||||
(<any>window).odoo.rootWidget = rootWidget;
|
||||
});
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import { findInTree } from "../core/utils";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export interface BaseMenuItem {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number | false;
|
||||
action: string | false;
|
||||
icon: string | false;
|
||||
children: BaseMenuItem[];
|
||||
}
|
||||
|
||||
export interface MenuItem extends BaseMenuItem {
|
||||
// root menu id
|
||||
menuId: number;
|
||||
actionId: number;
|
||||
children: MenuItem[];
|
||||
}
|
||||
|
||||
export interface MenuInfo {
|
||||
menuMap: { [key: number]: MenuItem | undefined };
|
||||
roots: number[];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generate a valid MenuInfo object from a list of BaseMenuItems. This function
|
||||
* is supposed to be called once at startup.
|
||||
*/
|
||||
export function processMenuItems(items: BaseMenuItem[]): MenuInfo {
|
||||
const menuMap: MenuInfo["menuMap"] = {};
|
||||
const roots: number[] = [];
|
||||
|
||||
// build MenuItems
|
||||
for (let root of items) {
|
||||
roots.push(root.id);
|
||||
addToMap(root, root.id);
|
||||
}
|
||||
|
||||
function addToMap(m: BaseMenuItem, menuId: number): MenuItem {
|
||||
let item: MenuItem = Object.assign({ menuId, actionId: -1 }, <MenuItem>m);
|
||||
menuMap[item.id] = item;
|
||||
item.children = m.children.map(c => addToMap(c, menuId));
|
||||
return item;
|
||||
}
|
||||
|
||||
// add proper actionId to every menuitems
|
||||
for (let menuId in menuMap) {
|
||||
const menu = menuMap[menuId]!;
|
||||
let menuWithAction = menu && findInTree(menu, m => Boolean(m.action));
|
||||
if (menuWithAction) {
|
||||
menu.actionId = parseInt(
|
||||
(<string>menuWithAction.action).split(",")[1],
|
||||
10
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { menuMap, roots };
|
||||
}
|
||||
|
||||
export function getAppAndAction(
|
||||
info: MenuInfo,
|
||||
query: { menu_id?: string; action_id?: string }
|
||||
): { app: MenuItem | null; actionId: number | null } {
|
||||
let app = findApp(info, query.menu_id, query.action_id);
|
||||
let actionId = findAction(info, query.menu_id, query.action_id);
|
||||
|
||||
return { app, actionId };
|
||||
}
|
||||
|
||||
function findApp(
|
||||
info: MenuInfo,
|
||||
menu_id?: string,
|
||||
action_id?: string
|
||||
): MenuItem | null {
|
||||
if (menu_id) {
|
||||
const menuID = parseInt(menu_id, 10);
|
||||
if (info.roots.indexOf(menuID) > -1) {
|
||||
const menu = info.menuMap[menuID];
|
||||
if (menu) {
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (action_id) {
|
||||
for (let itemID in info.menuMap) {
|
||||
let menu = info.menuMap[itemID];
|
||||
const actionId = menu && menu.actionId;
|
||||
if (actionId && String(actionId) === action_id) {
|
||||
if (menu) {
|
||||
let topMenu = info.menuMap[menu.menuId];
|
||||
if (topMenu) {
|
||||
return topMenu;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findAction(
|
||||
info: MenuInfo,
|
||||
menu_id?: string,
|
||||
action_id?: string
|
||||
): number | null {
|
||||
if (action_id) {
|
||||
return parseInt(action_id, 10);
|
||||
}
|
||||
if (menu_id) {
|
||||
const menuID = parseInt(menu_id, 10);
|
||||
const menu = info.menuMap[menuID]!;
|
||||
return menu.actionId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1,19 +1,42 @@
|
||||
import { INotification } from "../core/notifications";
|
||||
import { Widget } from "./widget";
|
||||
import { Query } from "../core/router";
|
||||
import { debounce } from "../core/utils";
|
||||
import { Env } from "./widget";
|
||||
import { MenuInfo, MenuItem, getAppAndAction } from "../misc/menu_helpers";
|
||||
import { ActionStack } from "../services/action_manager";
|
||||
import { ActionContainer } from "./action_container";
|
||||
import { HomeMenu } from "./home_menu";
|
||||
import { Navbar } from "./navbar";
|
||||
import { Notification } from "./notification";
|
||||
import { Query } from "../core/router";
|
||||
import { Env, Widget } from "./widget";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export interface MenuItem {
|
||||
id: number;
|
||||
name: string;
|
||||
parentId: number | false;
|
||||
action: string | false;
|
||||
icon: string | false;
|
||||
|
||||
// root menu id
|
||||
appId: number;
|
||||
actionId: number;
|
||||
children: MenuItem[];
|
||||
}
|
||||
|
||||
export interface MenuInfo {
|
||||
menuMap: { [key: number]: MenuItem | undefined };
|
||||
|
||||
// mapping from action id to menu id
|
||||
actionMap: { [id: number]: number | undefined };
|
||||
roots: number[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
menuInfo: MenuInfo;
|
||||
}
|
||||
|
||||
interface State {
|
||||
notifications: INotification[];
|
||||
stack: ActionStack;
|
||||
@@ -21,10 +44,6 @@ interface State {
|
||||
currentApp: MenuItem | null;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
menuInfo: MenuInfo;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Root Widget
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -43,7 +62,7 @@ export class Root extends Widget<Props, State> {
|
||||
constructor(env: Env, props: Props) {
|
||||
super(env, props);
|
||||
const query = this.env.router.getQuery();
|
||||
let { app, actionId } = getAppAndAction(props.menuInfo, query);
|
||||
let { app, actionId } = this.getAppAndAction(query);
|
||||
this.state.currentApp = app;
|
||||
if (!actionId) {
|
||||
this.state.inHome = true;
|
||||
@@ -73,7 +92,7 @@ export class Root extends Widget<Props, State> {
|
||||
}
|
||||
|
||||
private updateAction(query: Query) {
|
||||
let { app, actionId } = getAppAndAction(this.props.menuInfo, query);
|
||||
let { app, actionId } = this.getAppAndAction(query);
|
||||
this.updateAppState(app, actionId);
|
||||
}
|
||||
|
||||
@@ -81,7 +100,7 @@ export class Root extends Widget<Props, State> {
|
||||
const newApp = app || this.state.currentApp;
|
||||
if (actionId) {
|
||||
const query: Query = { action_id: String(actionId) };
|
||||
const menuId = newApp ? newApp.menuId : false;
|
||||
const menuId = newApp ? newApp.appId : false;
|
||||
if (menuId) {
|
||||
query.menu_id = String(menuId);
|
||||
}
|
||||
@@ -100,7 +119,34 @@ export class Root extends Widget<Props, State> {
|
||||
}
|
||||
|
||||
openMenu(menu: MenuItem) {
|
||||
const app = this.props.menuInfo.menuMap[menu.menuId]!;
|
||||
const app = this.props.menuInfo.menuMap[menu.appId]!;
|
||||
this.updateAppState(app, menu.actionId);
|
||||
}
|
||||
|
||||
private getAppAndAction(
|
||||
query: Query
|
||||
): { app: MenuItem | null; actionId: number | null } {
|
||||
const menuInfo = this.props.menuInfo;
|
||||
let app: MenuItem | null = null;
|
||||
let actionId: number | null = null;
|
||||
if ("action_id" in query) {
|
||||
actionId = parseInt(query.action_id, 10);
|
||||
if (menuInfo.actionMap[actionId]) {
|
||||
const menuId = menuInfo.actionMap[actionId]!;
|
||||
const appId = menuInfo.menuMap[menuId]!.appId;
|
||||
app = menuInfo.menuMap[appId]!;
|
||||
}
|
||||
}
|
||||
if ("menu_id" in query) {
|
||||
const menuId = parseInt(query.menu_id, 10);
|
||||
const menu = menuInfo.menuMap[menuId];
|
||||
if (menu) {
|
||||
app = menuInfo.menuMap[menu.appId] || null;
|
||||
if (!actionId) {
|
||||
actionId = menu.actionId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { app, actionId };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user