refactoring (action management is now applied in root widget)

This commit is contained in:
Géry Debongnie
2019-03-04 12:52:14 +01:00
parent 0983ff0d24
commit 3a761da764
12 changed files with 75 additions and 196 deletions
-13
View File
@@ -30,16 +30,3 @@ document.addEventListener("DOMContentLoaded", async function() {
// For debugging purpose, we keep a reference to the root widget in odoo
(<any>window).odoo.rootWidget = rootWidget;
});
let testMixin = Base =>
class extends Base {
calc() {
return 32;
}
};
class Foo {}
class Bar extends testMixin(Foo) {}
console.log(new Bar().calc());
+45 -33
View File
@@ -49,7 +49,13 @@ export type ActionDescription =
| ActWindowActionDescription;
export type ActionInfo = ClientActionInfo | ActWindowInfo;
export type ActionStack = ActionInfo[];
export interface Action {
id: number;
widget?: Widget<any, any>;
executor(parent: Widget<any, any>): Promise<Widget<any, any> | null>;
activate(): void;
}
//------------------------------------------------------------------------------
// Action Manager Mixin
@@ -60,55 +66,61 @@ export function actionManagerMixin<T extends ReturnType<typeof rpcMixin>>(
) {
return class extends Base {
actionCache: { [key: number]: Promise<ActionDescription> } = {};
currentAction?: Action;
lastAction?: Action;
async doAction(request: ActionRequest) {
const self = this;
const descr = await this.loadAction(request);
let executor;
switch (descr.type) {
case "ir.actions.client":
return this.doClientAction(descr);
executor = this.doClientAction(descr);
break;
case "ir.actions.act_window":
return this.doActWindowAction(descr);
executor = this.doActWindowAction(descr);
break;
default:
throw new Error("unhandled action");
}
const action: Action = {
id: this.generateID(),
executor,
activate() {
if (self.currentAction && self.currentAction.widget) {
self.currentAction.widget.destroy();
}
self.currentAction = action;
self.update({
inHome: false
});
document.title = descr.name + " - Odoo";
}
};
self.lastAction = action;
this.trigger("update_action", action);
}
doActWindowAction(descr: ActWindowActionDescription) {
let title = descr.name;
this.update({
inHome: false,
stack: [
{
id: 1,
context: {},
target: "current",
type: "act_window",
title,
Widget: View
}
]
});
document.title = descr.name + " - Odoo";
return async function executor(this: Action, parent: Widget<any, any>) {
const widget = new View(parent, {});
const div = document.createElement("div");
await widget.mount(div);
this.widget = widget;
return widget;
};
}
doClientAction(descr: ClientActionDescription) {
let key = descr.tag;
let title = descr.name;
let Widget = this.actionRegistry.get(key);
this.update({
inHome: false,
stack: [
{
id: 1,
context: {},
target: "current",
type: "client",
title,
Widget: Widget
}
]
});
document.title = descr.name + " - Odoo";
return async function executor(this: Action, parent: Widget<any, any>) {
const widget = new Widget(parent, {});
const div = document.createElement("div");
await widget.mount(div);
this.widget = widget;
return widget;
};
}
loadAction(id: number): Promise<ActionDescription> {
@@ -1,6 +1,5 @@
import { Type } from "../core/component";
import { BaseStore } from "./store";
import { idGenerator } from "../core/utils";
//------------------------------------------------------------------------------
// Notifications Mixin
@@ -15,8 +14,6 @@ export interface INotification {
export function notificationMixin<T extends Type<BaseStore>>(Base: T) {
return class extends Base {
generateID = idGenerator();
addNotification(notif: Partial<INotification>): number {
const id = this.generateID();
const defaultVals = {
+4 -9
View File
@@ -1,12 +1,9 @@
import { EventBus } from "../core/event_bus";
import { Registry } from "../core/registry";
import { idGenerator } from "../core/utils";
import { RPC } from "../services/ajax";
import { IRouter, Query } from "../services/router";
import {
actionManagerMixin,
ActionStack,
ActionWidget
} from "./action_manager_mixin";
import { actionManagerMixin, ActionWidget } from "./action_manager_mixin";
import { notificationMixin } from "./notification_mixin";
import { rpcMixin } from "./rpc_mixin";
import { MenuItem } from "./store";
@@ -15,7 +12,7 @@ import { MenuItem } from "./store";
// Types
//------------------------------------------------------------------------------
export { ActionStack, ActionWidget } from "./action_manager_mixin";
export { ActionWidget } from "./action_manager_mixin";
export { INotification } from "./notification_mixin";
export { RPC } from "./rpc_mixin";
@@ -40,7 +37,6 @@ export interface MenuInfo {
}
export interface State {
stack: ActionStack;
inHome: boolean;
currentApp: MenuItem | null;
}
@@ -55,7 +51,6 @@ export interface Services {
//------------------------------------------------------------------------------
export class BaseStore extends EventBus {
state: State = {
stack: [],
inHome: false,
currentApp: null
};
@@ -63,6 +58,7 @@ export class BaseStore extends EventBus {
services: Services;
actionRegistry: Registry<ActionWidget>;
currentQuery: Query;
generateID = idGenerator();
constructor(
services: Services,
@@ -115,7 +111,6 @@ export class Store extends actionManagerMixin(
this.state.inHome = true;
this.services.router.navigate({ home: true });
}
this.services.router.on("query_changed", this, this.updateAction);
this.updateAction(this.services.router.getQuery());
}
@@ -1,51 +0,0 @@
import { ActionStack } from "../store/store";
import { Widget } from "./widget";
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
export interface Props {
stack: ActionStack;
}
//------------------------------------------------------------------------------
// Action Container
//------------------------------------------------------------------------------
export class ActionContainer extends Widget<Props, {}> {
template = "web.action_container";
currentWidget: any;
willStart() {
return this.setContentWidget();
}
mounted() {
if (this.currentWidget && this.currentWidget.el) {
this.el!.appendChild(this.currentWidget.el);
this.currentWidget.__mount();
}
}
shouldUpdate(nextProps: Props) {
if (nextProps.stack !== this.props.stack) {
this.props = nextProps;
this.setContentWidget();
}
return false;
}
async setContentWidget() {
const info = this.props.stack[this.props.stack.length - 1];
if (info) {
const Widget = info.Widget;
let widget = new Widget(this, {});
await widget.mount(this.el || document.createElement("div"));
if (this.currentWidget) {
this.currentWidget.destroy();
}
this.currentWidget = widget;
}
}
}
+18 -2
View File
@@ -1,11 +1,11 @@
import { debounce } from "../core/utils";
import { Env } from "../env";
import { State, Store } from "../store/store";
import { ActionContainer } from "./action_container";
import { HomeMenu } from "./home_menu";
import { Navbar } from "./navbar";
import { Notification } from "./notification";
import { Widget } from "./widget";
import { Action } from "../store/action_manager_mixin";
//------------------------------------------------------------------------------
// Root Widget
@@ -13,7 +13,7 @@ import { Widget } from "./widget";
export class Root extends Widget<Store, State> {
template = "web.web_client";
widgets = { Navbar, HomeMenu, ActionContainer };
widgets = { Navbar, HomeMenu };
notifications: { [id: number]: Notification } = {};
store: Store;
@@ -53,5 +53,21 @@ export class Root extends Widget<Store, State> {
this.render();
}
}, 50));
// actions
this.store.on("update_action", this, this.applyAction);
if (this.store.lastAction) {
this.applyAction(this.store.lastAction);
}
}
async applyAction(action: Action) {
const widget = await action.executor(this);
if (widget) {
// to do: call some public method of widget instead...
(<HTMLElement>this.refs.content).appendChild(widget.el!);
widget.__mount();
action.activate();
}
}
}
+1 -3
View File
@@ -4,9 +4,7 @@
<div t-name="web.web_client" class="o_web_client">
<t t-widget="Navbar" t-props="{inHome:state.inHome,app:state.currentApp}" />
<t t-widget="HomeMenu" t-if="state.inHome" t-keep-alive="1" t-props="{menuInfo:props.menuInfo}" />
<t t-else="1">
<t t-widget="ActionContainer" t-props="{stack:state.stack}" t-keep-alive="1"/>
</t>
<div class="o_content" t-att-class="state.inHome ? 'o_hidden' : ''" t-ref="content"/>
<div class="o_notification_container" t-ref="notification_container"/>
<div class="o_loading d-none" t-ref="loading_indicator">Loading</div>
</div>