move code in init file, remove menu helpers

This commit is contained in:
Géry Debongnie
2019-02-05 16:08:35 +01:00
parent abbf077977
commit 5f49f82a04
5 changed files with 198 additions and 198 deletions
-53
View File
@@ -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
};
});
+134
View File
@@ -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 };
}
+6 -10
View File
@@ -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;
});
-123
View File
@@ -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;
}
+58 -12
View File
@@ -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 };
}
}