diff --git a/web/static/src/scss/app.scss b/web/static/src/scss/app.scss
index 5dcd58b0..0ed8fbd4 100644
--- a/web/static/src/scss/app.scss
+++ b/web/static/src/scss/app.scss
@@ -68,6 +68,15 @@ body {
margin: 5px 0 0 0;
background-color: $o-notification-info-bg-color;
box-shadow: 0px 0px 5px 1px $o-main-text-color;
+ position: relative;
+
+ .o_close {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ color: rgba(0, 0, 0, 0.3);
+ text-decoration: none;
+ }
.o_notification_title {
display: flex;
diff --git a/web/static/src/ts/root.ts b/web/static/src/ts/root.ts
index d5154057..afbff914 100644
--- a/web/static/src/ts/root.ts
+++ b/web/static/src/ts/root.ts
@@ -8,7 +8,7 @@ import { Env } from "./env";
const template = `
-
+
@@ -27,7 +27,9 @@ export class Root extends Widget {
mounted() {
this.env.actionManager.on("action_ready", this, this.setContentWidget);
- this.env.notifications.on("notification_added", this, this.addNotification);
+ this.env.notifications.on("notification_added", this, this.addNotif);
+ this.env.notifications.on("notification_removed", this, this.removeNotif);
+
const actionWidget = this.env.actionManager.getCurrentAction();
if (actionWidget) {
this.setContentWidget(actionWidget);
@@ -44,8 +46,12 @@ export class Root extends Widget {
this.content = newWidget;
}
- addNotification(notif: INotification) {
+ addNotif(notif: INotification) {
const notifications = this.state.notifications.concat(notif);
this.updateState({ notifications });
}
+ removeNotif(notif: INotification) {
+ const notifs = this.state.notifications.filter(f => f.id !== notif.id);
+ this.updateState({ notifications: notifs });
+ }
}
diff --git a/web/static/src/ts/services/notifications.ts b/web/static/src/ts/services/notifications.ts
index 44a52c53..24da7d91 100644
--- a/web/static/src/ts/services/notifications.ts
+++ b/web/static/src/ts/services/notifications.ts
@@ -12,7 +12,7 @@ export interface INotification {
sticky: boolean;
}
-export type NotificationEvent = "notification_added" | "notification_closed";
+export type NotificationEvent = "notification_added" | "notification_removed";
export type Callback = (notif: INotification) => void;
@@ -40,13 +40,16 @@ export class NotificationManager extends Bus implements INotificationManager {
const notification = Object.assign(defaultVals, notif, { id });
this.notifications[id] = notification;
this.trigger("notification_added", notification);
+ if (!notification.sticky) {
+ setTimeout(() => this.close(id), 2500);
+ }
return id;
}
close(id: number) {
let notification = this.notifications[id];
if (notification) {
delete this.notifications[id];
- this.trigger("notification_closed", notification);
+ this.trigger("notification_removed", notification);
}
}
}
diff --git a/web/static/src/ts/widgets/Discuss.ts b/web/static/src/ts/widgets/Discuss.ts
index c435af51..fc82bf81 100644
--- a/web/static/src/ts/widgets/Discuss.ts
+++ b/web/static/src/ts/widgets/Discuss.ts
@@ -20,7 +20,8 @@ const template = `
-
+
+
`;
@@ -54,10 +55,10 @@ export class Discuss extends Widget
{
this.updateState({ color: newColor });
}
- addNotif() {
+ addNotif(sticky: boolean) {
const text = (this.refs.textinput).value;
const message = `It is now ${new Date().toLocaleTimeString()}.
Msg: ${text}`;
- this.env.notifications.add({ title: "hey", message: message });
+ this.env.notifications.add({ title: "hey", message: message, sticky });
}
}
diff --git a/web/static/tests/helpers.ts b/web/static/tests/helpers.ts
index 8582597d..204520e7 100644
--- a/web/static/tests/helpers.ts
+++ b/web/static/tests/helpers.ts
@@ -2,7 +2,9 @@ import { QWeb } from "../src/ts/core/qweb_vdom";
import { idGenerator } from "../src/ts/core/utils";
import { WEnv } from "../src/ts/core/widget";
import { Env } from "../src/ts/env";
-import { IAjax, RPC } from "../src/ts/services/ajax";
+import { IAjax, RPCQuery } from "../src/ts/services/ajax";
+import { NotificationManager } from "../src/ts/services/notifications";
+
import {
IActionManager,
ActionEvent,
@@ -28,12 +30,14 @@ export function makeTestEnv(): Env {
const ajax = new MockAjax();
const actionManager = new MockActionManager();
const router = new MockRouter();
+ const notifications = new NotificationManager();
let { qweb, getID } = makeTestWEnv();
return {
qweb,
getID,
ajax,
actionManager,
+ notifications,
router,
rpc: ajax.rpc,
debug: false,
@@ -42,7 +46,7 @@ export function makeTestEnv(): Env {
}
class MockAjax implements IAjax {
- async rpc(rpc: RPC) {
+ async rpc(rpc: RPCQuery) {
return true;
}
}
diff --git a/web/static/tests/services/notifications.test.ts b/web/static/tests/services/notifications.test.ts
index f53d13bd..bab35b60 100644
--- a/web/static/tests/services/notifications.test.ts
+++ b/web/static/tests/services/notifications.test.ts
@@ -36,7 +36,7 @@ test("can subscribe and add notification", () => {
test("can close a notification", () => {
const notifications: INotificationManager = new NotificationManager();
let notified = false;
- notifications.on("notification_closed", {}, () => (notified = true));
+ notifications.on("notification_removed", {}, () => (notified = true));
const id = notifications.add(makeNotification());
expect(notified).toBe(false);
@@ -44,3 +44,31 @@ test("can close a notification", () => {
notifications.close(id);
expect(notified).toBe(true);
});
+
+test("notifications closes themselves after a while", () => {
+ jest.useFakeTimers();
+ const notifications: INotificationManager = new NotificationManager();
+ let removed = false;
+ notifications.on("notification_removed", {}, () => (removed = true));
+
+ notifications.add(makeNotification());
+
+ expect(setTimeout).toHaveBeenCalledTimes(1);
+ expect(removed).toBe(false);
+ jest.runAllTimers();
+ expect(removed).toBe(true);
+});
+
+test("sticky notifications do not close themselves after a while", () => {
+ jest.useFakeTimers();
+ const notifications: INotificationManager = new NotificationManager();
+ let removed = false;
+ notifications.on("notification_removed", {}, () => (removed = true));
+
+ notifications.add(makeNotification({ sticky: true }));
+
+ expect(setTimeout).toHaveBeenCalledTimes(0);
+ expect(removed).toBe(false);
+ jest.runAllTimers();
+ expect(removed).toBe(false);
+});