mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
work on initialization
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import { QWeb } from "./core/qweb_vdom";
|
||||
import { idGenerator, memoize } from "./core/utils";
|
||||
import { TemplateLoader } from "./loaders/templates";
|
||||
import { actionRegistry } from "./registries";
|
||||
import { RPC } from "./services/ajax";
|
||||
import { ActionManager } from "./store/action_manager";
|
||||
import { Ajax } from "./store/ajax";
|
||||
import { NotificationManager } from "./store/notifications";
|
||||
import { Router } from "./store/router";
|
||||
import { Env } from "./widgets/widget";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
interface Loaders {
|
||||
loadTemplates: TemplateLoader;
|
||||
}
|
||||
|
||||
interface Services {
|
||||
rpc: RPC;
|
||||
}
|
||||
|
||||
type EnvBuilder = (loaders: Loaders, services: Services) => Promise<Env>;
|
||||
//------------------------------------------------------------------------------
|
||||
// Environment
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* init 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 init is memoized: every call to
|
||||
* this function will actually return the same environment.
|
||||
*/
|
||||
export const makeEnv: EnvBuilder = memoize(_makeEnv);
|
||||
|
||||
async function _makeEnv(loaders: Loaders, services: Services): Promise<Env> {
|
||||
// services
|
||||
const qweb = new QWeb();
|
||||
const router = new Router();
|
||||
const ajax = new Ajax(services.rpc);
|
||||
const actionManager = new ActionManager(actionRegistry, ajax);
|
||||
const notifications = new NotificationManager();
|
||||
|
||||
// templates
|
||||
const templates = await loaders.loadTemplates();
|
||||
qweb.addTemplate("default", "<div/>");
|
||||
qweb.loadTemplates(templates);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Ajax } from "./store/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 "./store/action_manager";
|
||||
import { MenuInfo, MenuItem } from "./widgets/root";
|
||||
import { Env } from "./widgets/widget";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
interface InitializedData {
|
||||
env: Env;
|
||||
menuInfo: MenuInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* init 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 init 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(ajaxFetch);
|
||||
const actionManager = new ActionManager(actionRegistry, ajax);
|
||||
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 ajaxFetch(route: string, params: any): Promise<any> {
|
||||
console.log("RPC", route, params);
|
||||
const delay = Math.random() * 150;
|
||||
return new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load xml templates as a string.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all menu items
|
||||
*/
|
||||
function loadMenus(): MenuInfo {
|
||||
const menuItems: BaseMenuItem[] = (<any>window).odoo.menus;
|
||||
if (!menuItems) {
|
||||
throw new Error("Cannot find menus description");
|
||||
}
|
||||
const menuInfo = getMenuInfo(menuItems);
|
||||
delete (<any>window).odoo.menus; // overkill?
|
||||
return menuInfo;
|
||||
}
|
||||
|
||||
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 menus: { [key: number]: MenuItem | undefined } = {};
|
||||
const actionMap: { [id: number]: MenuItem | undefined } = {};
|
||||
const roots: number[] = [];
|
||||
|
||||
// build MenuItems
|
||||
for (let root of items) {
|
||||
roots.push(root.id);
|
||||
addToMap(root);
|
||||
}
|
||||
|
||||
function addToMap(m: BaseMenuItem, app?: MenuItem): MenuItem {
|
||||
const item: Partial<MenuItem> = {
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
parentId: m.parent_id,
|
||||
action: m.action,
|
||||
icon: m.icon,
|
||||
actionId: -1 // will be filled later on with correct id
|
||||
};
|
||||
item.app = app || (item as MenuItem);
|
||||
item.children = m.children.map(c => addToMap(c, item.app as MenuItem));
|
||||
menus[item.id!] = item as MenuItem;
|
||||
return item as MenuItem;
|
||||
}
|
||||
|
||||
// add proper actionId to every menuitems
|
||||
for (let menuId in menus) {
|
||||
const menu = menus[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { menus, roots, actionMap };
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { findInTree } from "../core/utils";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export interface MenuItem {
|
||||
id: number;
|
||||
name: string;
|
||||
parentId: number | false;
|
||||
action: string | false;
|
||||
icon: string | false;
|
||||
|
||||
// root menu id
|
||||
app: MenuItem;
|
||||
actionId: number;
|
||||
children: MenuItem[];
|
||||
}
|
||||
|
||||
export interface MenuInfo {
|
||||
menus: { [key: number]: MenuItem | undefined };
|
||||
|
||||
actionMap: { [id: number]: MenuItem | undefined };
|
||||
roots: number[];
|
||||
}
|
||||
|
||||
interface BaseMenuItem {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number | false;
|
||||
action: string | false;
|
||||
icon: string | false;
|
||||
children: BaseMenuItem[];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Code
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load all menu items
|
||||
*/
|
||||
export function loadMenus(): MenuInfo {
|
||||
const menuItems: BaseMenuItem[] = (<any>window).odoo.menus;
|
||||
if (!menuItems) {
|
||||
throw new Error("Cannot find menus description");
|
||||
}
|
||||
const menuInfo = getMenuInfo(menuItems);
|
||||
delete (<any>window).odoo.menus; // overkill?
|
||||
return menuInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 menus: { [key: number]: MenuItem | undefined } = {};
|
||||
const actionMap: { [id: number]: MenuItem | undefined } = {};
|
||||
const roots: number[] = [];
|
||||
|
||||
// build MenuItems
|
||||
for (let root of items) {
|
||||
roots.push(root.id);
|
||||
addToMap(root);
|
||||
}
|
||||
|
||||
function addToMap(m: BaseMenuItem, app?: MenuItem): MenuItem {
|
||||
const item: Partial<MenuItem> = {
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
parentId: m.parent_id,
|
||||
action: m.action,
|
||||
icon: m.icon,
|
||||
actionId: -1 // will be filled later on with correct id
|
||||
};
|
||||
item.app = app || (item as MenuItem);
|
||||
item.children = m.children.map(c => addToMap(c, item.app as MenuItem));
|
||||
menus[item.id!] = item as MenuItem;
|
||||
return item as MenuItem;
|
||||
}
|
||||
|
||||
// add proper actionId to every menuitems
|
||||
for (let menuId in menus) {
|
||||
const menu = menus[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { menus, roots, actionMap };
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export type TemplateLoader = () => Promise<string>;
|
||||
|
||||
/**
|
||||
* Load xml templates as a string.
|
||||
*/
|
||||
export const loadTemplates: TemplateLoader = async function(): Promise<string> {
|
||||
const result = await fetch("templates.xml");
|
||||
if (!result.ok) {
|
||||
throw new Error("Error while fetching xml templates");
|
||||
}
|
||||
return result.text();
|
||||
};
|
||||
@@ -1,6 +1,9 @@
|
||||
///<amd-module name="main" />
|
||||
|
||||
import { init } from "./init";
|
||||
import { makeEnv } from "./env";
|
||||
import { rpc } from "./services/ajax";
|
||||
import { loadMenus } from "./loaders/menus";
|
||||
import { loadTemplates } from "./loaders/templates";
|
||||
import { Root } from "./widgets/root";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -8,7 +11,14 @@ import { Root } from "./widgets/root";
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async function() {
|
||||
const { env, menuInfo } = await init();
|
||||
const services = {
|
||||
rpc
|
||||
};
|
||||
const loaders = {
|
||||
loadTemplates
|
||||
};
|
||||
const env = await makeEnv(loaders, services);
|
||||
const menuInfo = loadMenus();
|
||||
|
||||
// Creating root widget
|
||||
const rootWidget = new Root(env, { menuInfo });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Registry } from "./core/registry";
|
||||
import { ActionWidget } from "./services/action_manager";
|
||||
import { ActionWidget } from "./store/action_manager";
|
||||
import { CRM } from "./widgets/crm";
|
||||
import { Discuss } from "./widgets/discuss";
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export type RPC = (route: string, params: any) => Promise<any>;
|
||||
|
||||
export const rpc: RPC = async function(route, params) {
|
||||
console.log("RPC", route, params);
|
||||
const delay = Math.random() * 150;
|
||||
return new Promise(resolve => setTimeout(resolve, delay));
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MenuItem } from "./root";
|
||||
import { MenuItem } from "../loaders/menus";
|
||||
import { PureWidget } from "./widget";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ActionStack } from "../services/action_manager";
|
||||
import { ActionStack } from "../store/action_manager";
|
||||
import { Widget } from "./widget";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MenuInfo, MenuItem } from "../widgets/root";
|
||||
import { MenuInfo, MenuItem } from "../loaders/menus";
|
||||
import { Widget } from "./widget";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INotification } from "../core/notifications";
|
||||
import { INotification } from "../store/notifications";
|
||||
import { Widget } from "./widget";
|
||||
|
||||
export class Notification extends Widget<INotification, {}> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Query } from "../core/router";
|
||||
import { Query } from "../store/router";
|
||||
import { debounce } from "../core/utils";
|
||||
import { ActionStack } from "../services/action_manager";
|
||||
import { MenuInfo, MenuItem } from "../loaders/menus";
|
||||
import { ActionStack } from "../store/action_manager";
|
||||
import { ActionContainer } from "./action_container";
|
||||
import { HomeMenu } from "./home_menu";
|
||||
import { Navbar } from "./navbar";
|
||||
@@ -11,26 +12,6 @@ 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
|
||||
app: MenuItem;
|
||||
actionId: number;
|
||||
children: MenuItem[];
|
||||
}
|
||||
|
||||
export interface MenuInfo {
|
||||
menus: { [key: number]: MenuItem | undefined };
|
||||
|
||||
actionMap: { [id: number]: MenuItem | undefined };
|
||||
roots: number[];
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
menuInfo: MenuInfo;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { IAjax } from "../services/ajax";
|
||||
import { IAjax } from "../store/ajax";
|
||||
import { Component, PureComponent, WEnv } from "../core/component";
|
||||
import { INotificationManager } from "../core/notifications";
|
||||
import { INotificationManager } from "../store/notifications";
|
||||
import { Registry } from "../core/registry";
|
||||
import { IRouter } from "../core/router";
|
||||
import { ActionWidget, IActionManager } from "../services/action_manager";
|
||||
import { IRouter } from "../store/router";
|
||||
import { ActionWidget, IActionManager } from "../store/action_manager";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NotificationManager } from "../../src/ts/core/notifications";
|
||||
import { NotificationManager } from "../../src/ts/store/notifications";
|
||||
|
||||
test("can subscribe and add notification", () => {
|
||||
let n = 0;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { readFile } from "fs";
|
||||
import { WEnv } from "../src/ts/core/component";
|
||||
import { Callback } from "../src/ts/core/event_bus";
|
||||
import { NotificationManager } from "../src/ts/core/notifications";
|
||||
import { NotificationManager } from "../src/ts/store/notifications";
|
||||
import { QWeb } from "../src/ts/core/qweb_vdom";
|
||||
import { IRouter, Query, RouterEvent } from "../src/ts/core/router";
|
||||
import { IRouter, Query, RouterEvent } from "../src/ts/store/router";
|
||||
import { idGenerator } from "../src/ts/core/utils";
|
||||
import { getMenuInfo } from "../src/ts/init";
|
||||
import { getMenuInfo, MenuInfo } from "../src/ts/loaders/menus";
|
||||
import { actionRegistry } from "../src/ts/registries";
|
||||
import { ActionManager } from "../src/ts/store/action_manager";
|
||||
import { Ajax } from "../src/ts/store/ajax";
|
||||
import { MenuInfo } from "../src/ts/widgets/root";
|
||||
import { Env } from "../src/ts/widgets/widget";
|
||||
|
||||
export function makeTestFixture() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ActionStack } from "../../src/ts/services/action_manager";
|
||||
import { ActionStack } from "../../src/ts/store/action_manager";
|
||||
import { ActionContainer, Props } from "../../src/ts/widgets/action_container";
|
||||
import { Widget } from "../../src/ts/widgets/widget";
|
||||
import * as helpers from "../helpers";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Navbar, Props } from "../../src/ts/widgets/navbar";
|
||||
import * as helpers from "../helpers";
|
||||
import { MenuInfo } from "../../src/ts/widgets/root";
|
||||
import { MenuInfo } from "../../src/ts/loaders/menus";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INotification } from "../../src/ts/core/notifications";
|
||||
import { INotification } from "../../src/ts/store/notifications";
|
||||
import { Notification } from "../../src/ts/widgets/notification";
|
||||
import { makeTestEnv, makeTestFixture, loadTemplates } from "../helpers";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user