1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
odoo.define('bus.BusService', function (require) {
"use strict";
var CrossTab = require('bus.CrossTab');
var core = require('web.core');
var ServicesMixin = require('web.ServicesMixin');
const session = require('web.session');
var BusService = CrossTab.extend(ServicesMixin, {
dependencies : ['local_storage'],
// properties
_audio: null,
/**
* As the BusService doesn't extend AbstractService, we have to replicate
* here what is done in AbstractService
*
* @param {Object} env
*/
init: function (env) {
this.env = env;
this._super();
},
/**
* Replicate the behavior of AbstractService:
*
* Directly calls the requested service, instead of triggering a
* 'call_service' event up, which wouldn't work as services have no parent.
*
* @param {OdooEvent} ev
*/
_trigger_up: function (ev) {
if (ev.name === 'call_service') {
const payload = ev.data;
let args = payload.args || [];
if (payload.service === 'ajax' && payload.method === 'rpc') {
// ajax service uses an extra 'target' argument for rpc
args = args.concat(ev.target);
}
const service = this.env.services[payload.service];
const result = service[payload.method].apply(service, args);
payload.callback(result);
}
},
/**
* This method is necessary in order for this Class to be used to instantiate services
*
* @abstract
*/
start: function () {},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Send a notification, and notify once per browser's tab
*
* @param {string} title
* @param {string} content
* @param {function} [callback] if given callback will be called when user clicks on notification
*/
sendNotification: function (title, content, callback) {
if (window.Notification && Notification.permission === "granted") {
if (this.isMasterTab()) {
try {
this._sendNativeNotification(title, content, callback);
} catch (error) {
// Notification without Serviceworker in Chrome Android doesn't works anymore
// So we fallback to do_notify() in this case
// https://bugs.chromium.org/p/chromium/issues/detail?id=481856
if (error.message.indexOf('ServiceWorkerRegistration') > -1) {
this.do_notify(title, content);
this._beep();
} else {
throw error;
}
}
}
} else {
this.do_notify(title, content);
if (this.isMasterTab()) {
this._beep();
}
}
},
/**
* Register listeners on notifications received on this bus service
*
* @param {Object} receiver
* @param {function} func
*/
onNotification: function () {
this.on.apply(this, ["notification"].concat(Array.prototype.slice.call(arguments)));
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Lazily play the 'beep' audio on sent notification
*
* @private
*/
_beep: function () {
if (typeof(Audio) !== "undefined") {
if (!this._audio) {
this._audio = new Audio();
var ext = this._audio.canPlayType("audio/ogg; codecs=vorbis") ? ".ogg" : ".mp3";
this._audio.src = session.url("/mail/static/src/audio/ting" + ext);
}
Promise.resolve(this._audio.play()).catch(_.noop);
}
},
/**
* Show a browser notification
*
* @private
* @param {string} title
* @param {string} content
* @param {function} [callback] if given callback will be called when user clicks on notification
*/
_sendNativeNotification: function (title, content, callback) {
var notification = new Notification(
// The native Notification API works with plain text and not HTML
// unescaping is safe because done only at the **last** step
_.unescape(title),
{
body: _.unescape(content),
icon: "/mail/static/src/img/odoobot_transparent.png"
});
notification.onclick = function () {
window.focus();
if (this.cancel) {
this.cancel();
} else if (this.close) {
this.close();
}
if (callback) {
callback();
}
};
},
});
core.serviceRegistry.add('bus_service', BusService);
return BusService;
});
|