3
0
Fork 0

[IMP] web_pwa_oca: Drop cache and refactor

12.0
Alexandre D. Díaz 2020-10-20 14:20:58 +02:00
parent 7473a485e9
commit e5c219f9f3
16 changed files with 294 additions and 277 deletions

View File

@ -31,6 +31,17 @@ Progressive Web Apps provide an installable, app-like experience on desktop and
They're web apps that are fast and reliable. And most importantly, they're web apps that work in any browser.
If you're building a web app today, you're already on the path towards building a Progressive Web App.
+ Developers Info.
The service worker is contructed using 'Odoo Class' to have the same class inheritance behaviour that in the 'user pages'. Be noticed
that 'Odoo Bootstrap' is not supported so, you can't use 'require' here.
All service worker content can be found in 'static/src/js/worker'. The management between 'user pages' and service worker is done in
'pwa_manager.js'.
The purpose of this module is give a base to make PWA applications.
**Table of contents**
.. contents::
@ -86,33 +97,43 @@ To use your PWA:
Known issues / Roadmap
======================
* Evaluate to extend ``FILES_TO_CACHE``
* Integrate `Notification API <https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification>`_
* Integrate `Web Share API <https://web.dev/web-share/>`_
* Create ``portal_pwa`` module, intended to be used by front-end users (customers, suppliers...)
* Current ``John Resig's inheritance`` implementation doesn't support ``async`` functions because ``this._super`` can't be called inside a promise. So we need use the following workaround:
- Natural 'async/await' example (This breaks "_super" call)
.. code-block:: javascript
* Current *John Resig's inheritance* implementation doesn't support ``async``
functions because ``this._super`` can't be called inside a promise. So we
need to use the following workaround:
var MyClass = OdooClass.extend({
myFunc: async function() {
- Natural 'async/await' example (This breaks "_super" call):
.. code-block:: javascript
var MyClass = OdooClass.extend({
myFunc: async function() {
const mydata = await ...do await stuff...
return mydata;
}
});
- Same code with the workaround:
.. code-block:: javascript
var MyClass = OdooClass.extend({
myFunc: function() {
return new Promise(async (resolve, reject) => {
const mydata = await ...do await stuff...
return mydata;
}
});
return resolve(mydata);
});
}
});
- Same code with the workaround:
.. code-block:: javascript
var MyClass = OdooClass.extend({
myFunc: function() {
return new Promise(async (resolve, reject) => {
const mydata = await ...do await stuff...
resolve(mydata);
});
}
});
* Fix issue when trying to run in localhost with several databases. The browser doesn't send the cookie and web manifest returns 404.
* Fix issue when trying to run in localhost with several databases. The browser
doesn't send the cookie and web manifest returns 404.
* Evaluate to support 'require' system.
* 'Install PWA' menu option disappears even if not installed. This is due to the
very nature of service workers, so they are running including when you close
the page tabs.
Bug Tracker
===========

View File

@ -9,38 +9,13 @@ from odoo.http import request, Controller, route
class PWA(Controller):
def _get_pwa_scripts(self):
""" Scripts to be imported in the service worker (Order is important) """
"""Scripts to be imported in the service worker (Order is important)"""
return [
"/web/static/lib/underscore/underscore.js",
"/web_pwa_oca/static/src/js/worker/libs/class.js",
"/web_pwa_oca/static/src/js/worker/base/tools.js",
"/web_pwa_oca/static/src/js/worker/base/cache_manager.js",
"/web_pwa_oca/static/src/js/worker/pwa.js",
]
def _get_asset_urls(self, asset_xml_id):
"""Get all urls that have 'asset_xml_id'"""
qweb_sudo = request.env["ir.qweb"].sudo()
assets = qweb_sudo._get_asset_nodes(asset_xml_id, {}, True, True)
urls = []
for asset in assets:
if asset[0] == "link":
urls.append(asset[1]["href"])
if asset[0] == "script":
urls.append(asset[1]["src"])
return urls
def _get_pwa_params(self):
"""Get javascript PWA class initialzation params"""
urls = []
urls.extend(self._get_asset_urls("web.assets_common"))
urls.extend(self._get_asset_urls("web.assets_backend"))
version_list = []
for url in urls:
version_list.append(url.split("/")[3])
cache_version = "-".join(version_list)
return [cache_version, urls]
@route("/service-worker.js", type="http", auth="public")
def render_service_worker(self):
"""Route to register the service worker in the 'main' scope ('/')"""
@ -53,6 +28,10 @@ class PWA(Controller):
headers=[("Content-Type", "text/javascript;charset=utf-8")],
)
def _get_pwa_params(self):
"""Get javascript PWA class initialzation params"""
return {}
def _get_pwa_manifest_icons(self, pwa_icon):
icons = []
if not pwa_icon:
@ -126,7 +105,7 @@ class PWA(Controller):
"name": pwa_name,
"short_name": pwa_short_name,
"icons": self._get_pwa_manifest_icons(pwa_icon),
"start_url": "/web",
"start_url": '/web',
"display": "standalone",
"background_color": background_color,
"theme_color": theme_color,
@ -137,5 +116,5 @@ class PWA(Controller):
"""Returns the manifest used to install the page as app"""
return request.make_response(
json.dumps(self._get_pwa_manifest()),
headers=[("Content-Type", "text/javascript;charset=utf-8")],
headers=[("Content-Type", "application/json;charset=utf-8")],
)

View File

@ -12,6 +12,7 @@ from odoo.tools.mimetypes import guess_mimetype
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
_pwa_icon_url_base = "/web_pwa_oca/icon"
pwa_name = fields.Char(
"Progressive Web App Name", help="Name of the Progressive Web Application"
@ -21,20 +22,20 @@ class ResConfigSettings(models.TransientModel):
help="Short Name of the Progressive Web Application",
)
pwa_icon = fields.Binary("Icon", readonly=False)
_pwa_icon_url_base = "/web_pwa_oca/icon"
pwa_background_color = fields.Char("Background Color")
pwa_theme_color = fields.Char("Theme Color")
@api.model
def get_values(self):
config_parameter_obj_sudo = self.env["ir.config_parameter"].sudo()
res = super(ResConfigSettings, self).get_values()
res["pwa_name"] = (
self.env["ir.config_parameter"]
.sudo()
.get_param("pwa.manifest.name", default="")
config_parameter_obj_sudo.get_param(
"pwa.manifest.name", default="Odoo PWA")
)
res["pwa_short_name"] = (
self.env["ir.config_parameter"]
.sudo()
.get_param("pwa.manifest.short_name", default="")
config_parameter_obj_sudo.get_param(
"pwa.manifest.short_name", default="Odoo")
)
pwa_icon_ir_attachment = (
self.env["ir.attachment"]
@ -44,6 +45,14 @@ class ResConfigSettings(models.TransientModel):
res["pwa_icon"] = (
pwa_icon_ir_attachment.datas if pwa_icon_ir_attachment else False
)
res["pwa_background_color"] = (
config_parameter_obj_sudo.get_param(
"pwa.manifest.background_color", default="#2E69B5")
)
res["pwa_theme_color"] = (
config_parameter_obj_sudo.get_param(
"pwa.manifest.theme_color", default="#2E69B5")
)
return res
def _unpack_icon(self, icon):
@ -89,13 +98,20 @@ class ResConfigSettings(models.TransientModel):
@api.model
def set_values(self):
config_parameter_obj_sudo = self.env["ir.config_parameter"].sudo()
res = super(ResConfigSettings, self).set_values()
self.env["ir.config_parameter"].sudo().set_param(
config_parameter_obj_sudo.set_param(
"pwa.manifest.name", self.pwa_name
)
self.env["ir.config_parameter"].sudo().set_param(
config_parameter_obj_sudo.set_param(
"pwa.manifest.short_name", self.pwa_short_name
)
config_parameter_obj_sudo.set_param(
"pwa.manifest.background_color", self.pwa_background_color
)
config_parameter_obj_sudo.set_param(
"pwa.manifest.theme_color", self.pwa_theme_color
)
# Retrieve previous value for pwa_icon from ir_attachment
pwa_icon_ir_attachments = (
self.env["ir.attachment"]

View File

@ -3,3 +3,14 @@ Make Odoo an installable Progressive Web Application.
Progressive Web Apps provide an installable, app-like experience on desktop and mobile that are built and delivered directly via the web.
They're web apps that are fast and reliable. And most importantly, they're web apps that work in any browser.
If you're building a web app today, you're already on the path towards building a Progressive Web App.
+ Developers Info.
The service worker is contructed using 'Odoo Class' to have the same class inheritance behaviour that in the 'user pages'. Be noticed
that 'Odoo Bootstrap' is not supported so, you can't use 'require' here.
All service worker content can be found in 'static/src/js/worker'. The management between 'user pages' and service worker is done in
'pwa_manager.js'.
The purpose of this module is give a base to make PWA applications.

View File

@ -1,27 +1,37 @@
* Evaluate to extend ``FILES_TO_CACHE``
* Integrate `Notification API <https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification>`_
* Integrate `Web Share API <https://web.dev/web-share/>`_
* Create ``portal_pwa`` module, intended to be used by front-end users (customers, suppliers...)
* Current ``John Resig's inheritance`` implementation doesn't support ``async`` functions because ``this._super`` can't be called inside a promise. So we need use the following workaround:
- Natural 'async/await' example (This breaks "_super" call)
.. code-block:: javascript
* Current *John Resig's inheritance* implementation doesn't support ``async``
functions because ``this._super`` can't be called inside a promise. So we
need to use the following workaround:
var MyClass = OdooClass.extend({
myFunc: async function() {
- Natural 'async/await' example (This breaks "_super" call):
.. code-block:: javascript
var MyClass = OdooClass.extend({
myFunc: async function() {
const mydata = await ...do await stuff...
return mydata;
}
});
- Same code with the workaround:
.. code-block:: javascript
var MyClass = OdooClass.extend({
myFunc: function() {
return new Promise(async (resolve, reject) => {
const mydata = await ...do await stuff...
return mydata;
}
});
return resolve(mydata);
});
}
});
- Same code with the workaround:
.. code-block:: javascript
var MyClass = OdooClass.extend({
myFunc: function() {
return new Promise(async (resolve, reject) => {
const mydata = await ...do await stuff...
resolve(mydata);
});
}
});
* Fix issue when trying to run in localhost with several databases. The browser doesn't send the cookie and web manifest returns 404.
* Fix issue when trying to run in localhost with several databases. The browser
doesn't send the cookie and web manifest returns 404.
* Evaluate to support 'require' system.
* 'Install PWA' menu option disappears even if not installed. This is due to the
very nature of service workers, so they are running including when you close
the page tabs.

View File

@ -372,6 +372,14 @@ ul.auto-toc {
<p>Progressive Web Apps provide an installable, app-like experience on desktop and mobile that are built and delivered directly via the web.
Theyre web apps that are fast and reliable. And most importantly, theyre web apps that work in any browser.
If youre building a web app today, youre already on the path towards building a Progressive Web App.</p>
<ul class="simple">
<li>Developers Info.</li>
</ul>
<p>The service worker is contructed using Odoo Class to have the same class inheritance behaviour that in the user pages. Be noticed
that Odoo Bootstrap is not supported so, you cant use require here.</p>
<p>All service worker content can be found in static/src/js/worker. The management between user pages and service worker is done in
pwa_manager.js.</p>
<p>The purpose of this module is give a base to make PWA applications.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
@ -431,20 +439,18 @@ And like all other installed apps, its a top level app in the task switcher.<
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id4">Known issues / Roadmap</a></h1>
<ul>
<li><p class="first">Evaluate to extend <tt class="docutils literal">FILES_TO_CACHE</tt></p>
</li>
<li><p class="first">Integrate <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification">Notification API</a></p>
</li>
<li><p class="first">Integrate <a class="reference external" href="https://web.dev/web-share/">Web Share API</a></p>
</li>
<li><p class="first">Create <tt class="docutils literal">portal_pwa</tt> module, intended to be used by front-end users (customers, suppliers…)</p>
</li>
<li><dl class="first docutils">
<dt>Current <tt class="docutils literal">John Resig's inheritance</tt> implementation doesnt support <tt class="docutils literal">async</tt> functions because <tt class="docutils literal">this._super</tt> cant be called inside a promise. So we need use the following workaround:</dt>
<dd><ul class="first last">
<li><dl class="first docutils">
<dt>Natural async/await example (This breaks “_super” call)</dt>
<dd><pre class="code javascript first last literal-block">
<li><p class="first">Current <em>John Resigs inheritance</em> implementation doesnt support <tt class="docutils literal">async</tt>
functions because <tt class="docutils literal">this._super</tt> cant be called inside a promise. So we
need to use the following workaround:</p>
<ul>
<li><p class="first">Natural async/await example (This breaks “_super” call):</p>
<pre class="code javascript literal-block">
<span class="kd">var</span> <span class="nx">MyClass</span> <span class="o">=</span> <span class="nx">OdooClass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<span class="nx">myFunc</span><span class="o">:</span> <span class="nx">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">mydata</span> <span class="o">=</span> <span class="nx">await</span> <span class="p">...</span><span class="k">do</span> <span class="nx">await</span> <span class="nx">stuff</span><span class="p">...</span>
@ -452,29 +458,29 @@ And like all other installed apps, its a top level app in the task switcher.<
<span class="p">}</span>
<span class="p">});</span>
</pre>
</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>Same code with the workaround:</dt>
<dd><pre class="code javascript first last literal-block">
<li><p class="first">Same code with the workaround:</p>
<pre class="code javascript literal-block">
<span class="kd">var</span> <span class="nx">MyClass</span> <span class="o">=</span> <span class="nx">OdooClass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<span class="nx">myFunc</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="nx">async</span> <span class="p">(</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">mydata</span> <span class="o">=</span> <span class="nx">await</span> <span class="p">...</span><span class="k">do</span> <span class="nx">await</span> <span class="nx">stuff</span><span class="p">...</span>
<span class="nx">resolve</span><span class="p">(</span><span class="nx">mydata</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">resolve</span><span class="p">(</span><span class="nx">mydata</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">});</span>
</pre>
</dd>
</dl>
</li>
</ul>
</dd>
</dl>
</li>
<li><p class="first">Fix issue when trying to run in localhost with several databases. The browser doesnt send the cookie and web manifest returns 404.</p>
<li><p class="first">Fix issue when trying to run in localhost with several databases. The browser
doesnt send the cookie and web manifest returns 404.</p>
</li>
<li><p class="first">Evaluate to support require system.</p>
</li>
<li><p class="first">Install PWA menu option disappears even if not installed. This is due to the
very nature of service workers, so they are running including when you close
the page tabs.</p>
</li>
</ul>
</div>

View File

@ -1,37 +1,39 @@
/* Copyright 2020 Lorenzo Battistini @ TAKOBI
Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define('web_pwa_oca.systray.install', function(require) {
odoo.define('web_pwa_oca.systray.install', function (require) {
"use strict";
var UserMenu = require('web.UserMenu');
var deferredInstallPrompt = null;
var WebClientObj = require("web.web_client");
UserMenu.include({
start: function() {
window.addEventListener('beforeinstallprompt', this.saveBeforeInstallPromptEvent);
return this._super.apply(this, arguments);
/**
* We can't control if the UserMenu is loaded berfore PWA manager...
* So check if need unhide the user menu options to install the PWA.
*
* @override
*/
start: function () {
var self = this;
return this._super.apply(this, arguments).then(function () {
if (WebClientObj.pwa_manager.canBeInstalled()) {
self.$el.find('#pwa_install_button')[0]
.removeAttribute('hidden');
}
});
},
saveBeforeInstallPromptEvent: function(evt) {
deferredInstallPrompt = evt;
this.$.find('#pwa_install_button')[0].removeAttribute('hidden');
},
_onMenuInstallpwa: function() {
deferredInstallPrompt.prompt();
// Hide the install button, it can't be called twice.
this.el.setAttribute('hidden', true);
// Log user response to prompt.
deferredInstallPrompt.userChoice
.then(function(choice) {
if (choice.outcome === 'accepted') {
console.log('User accepted the A2HS prompt', choice);
} else {
console.log('User dismissed the A2HS prompt', choice);
}
deferredInstallPrompt = null;
});
/**
* Handle 'Install PWA' user menu option click
*
* @private
*/
_onMenuInstallpwa: function () {
WebClientObj.pwa_manager.install();
},
});

View File

@ -1,34 +1,94 @@
/* Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define("web_pwa_oca.PWAManager", function(require) {
odoo.define("web_pwa_oca.PWAManager", function (require) {
"use strict";
var Class = require("web.Class");
var mixins = require("web.mixins");
var Widget = require("web.Widget");
var PWAManager = Class.extend(mixins.ParentedMixin, {
init: function() {
var PWAManager = Widget.extend({
_deferredInstallPrompt: null,
/**
* @override
*/
init: function () {
this._super.apply(this, arguments);
if (!('serviceWorker' in navigator)) {
throw new Error(
"This browser is not compatible with service workers");
}
this._service_worker = navigator.serviceWorker;
this.registerServiceWorker('/service-worker.js');
window.addEventListener(
'beforeinstallprompt', this._onBeforeInstallPrompt.bind(this));
},
/**
* @param {String} sw_script
* @param {Function} success_callback
* @returns {Promise}
*/
registerServiceWorker: function(sw_script, success_callback) {
registerServiceWorker: function (sw_script) {
return this._service_worker.register(sw_script)
.then(success_callback)
.catch(function(error) {
.then(this._onRegisterServiceWorker)
.catch(function (error) {
console.log('[ServiceWorker] Registration failed: ', error);
});
},
install: function () {
if (!this._deferredInstallPrompt) {
return;
}
var self = this;
var systray_menu = this.getParent().menu.systray_menu;
this._deferredInstallPrompt.prompt();
// Log user response to prompt.
this._deferredInstallPrompt.userChoice
.then(function (choice) {
if (choice.outcome === 'accepted') {
// Hide the install button, it can't be called twice.
systray_menu.$el.find('#pwa_install_button')
.attr('hidden', true);
self._deferredInstallPrompt = null;
console.log('User accepted the A2HS prompt', choice);
} else {
console.log('User dismissed the A2HS prompt', choice);
}
});
},
canBeInstalled: function () {
return !_.isNull(this._deferredInstallPrompt);
},
/**
* Handle PWA installation flow
*
* @private
* @param {BeforeInstallPromptEvent} evt
*/
_onBeforeInstallPrompt: function (evt) {
evt.preventDefault();
this._deferredInstallPrompt = evt;
// UserMenu can be loaded after this module
var menu = this.getParent().menu;
if (menu && menu.systray_menu) {
menu.systray_menu.$el.find('#pwa_install_button')[0]
.removeAttribute('hidden');
}
},
/**
* Need register some extra API? override this!
*
* @private
* @param {ServiceWorkerRegistration} registration
*/
_onRegisterServiceWorker: function (registration) {
console.log('[ServiceWorker] Registered:', registration);
},
});
return PWAManager;

View File

@ -1,30 +1,20 @@
/* Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define("web_pwa_oca.webclient", function(require) {
odoo.define("web_pwa_oca.webclient", function (require) {
"use strict";
var WebClient = require("web.WebClient");
var PWAManager = require("web_pwa_oca.PWAManager");
WebClient.include({
/**
* @override
*/
show_application: function() {
this.pwa_manager = new PWAManager();
this.pwa_manager.setParent(this);
this.pwa_manager.registerServiceWorker('/service-worker.js', this._onRegisterServiceWorker);
show_application: function () {
this.pwa_manager = new PWAManager(this);
return this._super.apply(this, arguments);
},
/**
* Need register some extra API? override this!
*
* @param {ServiceWorkerRegistration} registration
*/
_onRegisterServiceWorker: function(registration) {
console.log('[ServiceWorker] Registered:', registration);
},
});
});

View File

@ -1,45 +0,0 @@
/* Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
"use strict";
const CacheManager = OdooClass.extend({
init: function() {
this._cache = caches;
this._caches = {};
},
/**
* @returns {Promise[CacheStorage]}
*/
initCache: function(cache_name) {
if (_.has(this._caches, cache_name)) {
return Promise.resolve(this._caches[cache_name]);
}
return new Promise(async resolve => {
this._caches[cache_name] = await this._cache.open(cache_name);
resolve(this._caches[cache_name]);
});
},
/**
* @returns {CacheStorage}
*/
get: function(cache_name) {
return this._caches[cache_name];
},
/**
* @returns {Promise}
*/
clean: function() {
return this._cache.keys().then(keyList => {
return Promise.all(keyList.map(key => {
if (!_.has(this._caches, key)) {
console.log('[ServiceWorker] Removing old cache', key);
return this._cache.delete(key);
}
}));
});
},
});

View File

@ -1,17 +0,0 @@
/* Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
"use strict";
/**
* Execute various promises in sequential order and return a promise with all results
* Code from: https://decembersoft.com/posts/promises-in-serial-with-array-reduce/
*
* @param {Array[Promise]} tasks
* @returns {Promise[Array]}
*/
function execSequentialPromises(tasks) {
return tasks.reduce((promiseChain, currentTask) => {
return promiseChain.then(chainResults => currentTask.then(currentResult => [...chainResults, currentResult]));
}, Promise.resolve([]));
}

View File

@ -1,62 +1,35 @@
"use strict";
/* eslint strict: ["error", "global"] */
/* eslint-disable no-undef, no-empty-function, no-implicit-globals,
no-unused-vars */
/* Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
"use strict";
/**
* Services workers are a piece of software separated from the user page.
* Here can't use 'Odoo Bootstrap', so we can't work with 'require' system.
* When the service worker is called to be installed from the "pwa_manager"
* this class is instantiated.
*/
var PWA = OdooClass.extend({
/**
* @param {String} cache_name
* @param {Array[String]} prefetched_urls
*/
init: function (cache_name, prefetched_urls) {
this._cache_name = cache_name;
this._prefetched_urls = prefetched_urls;
this._cache = new CacheManager();
this._cacheLoadPromise = this._cache.initCache(this._cache_name);
// eslint-disable-next-line
init: function (params) {
// To be overridden
},
/**
* @returns {Promise}
*/
installWorker: function () {
return this._prefetchData();
return Promise.resolve();
},
/**
* @returns {Promise}
*/
activateWorker: function () {
return new Promise(async resolve => {
await this._cacheLoadPromise;
await this._cache.clean();
return resolve();
});
},
/**
* @returns {Promise[Response]}
*/
processRequest: function (request) {
if (request.method === 'GET' && (request.cache !== 'only-if-cached' || request.mode === 'same-origin')) {
// Strategy: Cache First
return this._cache.get(this._cache_name).match(request).then(function(response){
return response || fetch(request);
});
}
// Default browser behaviour
return fetch(request);
},
/**
* @returns {Promise}
*/
_prefetchData: function() {
// Prefetch URL's
return new Promise(resolve => {
this._cacheLoadPromise.then(async () => {
return resolve(await this._cache.get(this._cache_name).addAll(this._prefetched_urls));
});
});
return Promise.resolve();
},
});

View File

@ -3,14 +3,16 @@
<template id="web_layout_pwa" name="Web layout PWA" inherit_id="web.layout">
<xpath expr="//meta[@name='viewport']" position="after">
<!-- Add link rel manifest -->
<link rel="manifest" href="/web_pwa_oca/manifest.webmanifest"/>
<link rel="manifest" t-attf-href="/web_pwa_oca/manifest.webmanifest"/>
<!-- Add iOS meta tags and icons -->
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<meta name="apple-mobile-web-app-title" content="Odoo PWA"/>
<t t-set="pwa_name" t-value="request.env['ir.config_parameter'].sudo().get_param('pwa.manifest.name')"/>
<meta name="apple-mobile-web-app-title" t-att-content="pwa_name"/>
<link rel="apple-touch-icon" href="/web_pwa_oca/static/img/icons/icon-152x152.png"/>
<!-- Add meta theme-color -->
<meta name="theme-color" content="#2E69B5" />
<t t-set="pwa_theme_color" t-value="request.env['ir.config_parameter'].sudo().get_param('pwa.manifest.theme_color')"/>
<meta name="theme-color" t-att-content="pwa_theme_color" />
</xpath>
</template>
<template id="assets_backend" name="web service worker assets" inherit_id="web.assets_backend">

View File

@ -2,7 +2,7 @@
<odoo>
<template id="pwa_init" name="PWA Initialization">
const oca_pwa = new PWA(...<t t-esc="str(pwa_params)"/>);
const oca_pwa = new PWA(<t t-esc="str(pwa_params)"/>);
</template>
<template id="pwa_core_events" name="PWA Core Events">
<t t-set="evt_install">
@ -14,11 +14,10 @@
<t t-raw="evt_install" />
});
<t t-set="evt_fetch">
evt.respondWith(oca_pwa.processRequest(evt.request));
</t>
<!-- This is necessary to get PWA installable.
Other modules can add logic using 'evt_fetch' -->
<t t-set="evt_fetch" />
self.addEventListener('fetch', evt =&gt; {
console.log('[ServiceWorker] Fetching...');
<t t-raw="evt_fetch" />
});

View File

@ -66,7 +66,9 @@ class TestUi(odoo.tests.HttpCase):
self.assertEquals(manifest_content["icons"][0]["src"], "/web_pwa_oca/icon.svg")
self.assertTrue(manifest_content["icons"][0]["type"].startswith("image/svg"))
self.assertEquals(manifest_content["icons"][0]["sizes"], "128x128 144x144 152x152 192x192 256x256 512x512")
self.assertEquals(
manifest_content["icons"][0]["sizes"],
"128x128 144x144 152x152 192x192 256x256 512x512")
# Get the icon and compare it
icon_data = self.url_open("/web_pwa_oca/icon.svg")

View File

@ -9,29 +9,37 @@
<div id="emails" position='after'>
<h2>Progressive Web App</h2>
<div class="row mt16 o_settings_container" id="pwa_settings">
<div class="col-12 col-lg-6 o_setting_box" id="domain_setting">
<div class="o_setting_right_pane">
<label for="pwa_name" string="PWA Title"/>
<span class="fa fa-lg fa-globe"/>
<div class="text-muted">
Name and icon of your PWA
<div class="col-12 col-lg-6 o_setting_box" id="domain_setting">
<div class="o_setting_right_pane">
<label for="pwa_name" string="PWA Title"/>
<span class="fa fa-lg fa-globe"/>
<div class="text-muted">
Name and icon of your PWA
</div>
<div class="content-group">
<div class="row mt16">
<label class="col-lg-3 o_light_label" string="Name" for="pwa_name"/>
<field name="pwa_name"/>
</div>
<div class="content-group">
<div class="row mt16">
<label class="col-lg-3 o_light_label" string="Name" for="pwa_name"/>
<field name="pwa_name"/>
</div>
<div class="row mt16">
<label class="col-lg-3 o_light_label" string="Short Name" for="pwa_short_name"/>
<field name="pwa_short_name"/>
</div>
<div class="row">
<label class="col-lg-3 o_light_label" for="pwa_icon" />
<field name="pwa_icon" widget="image" class="float-left oe_avatar"/>
</div>
<div class="row mt16">
<label class="col-lg-3 o_light_label" string="Short Name" for="pwa_short_name"/>
<field name="pwa_short_name"/>
</div>
<div class="row">
<label class="col-lg-3 o_light_label" for="pwa_background_color"/>
<field name="pwa_background_color"/>
</div>
<div class="row">
<label class="col-lg-3 o_light_label" for="pwa_theme_color"/>
<field name="pwa_theme_color"/>
</div>
<div class="row">
<label class="col-lg-3 o_light_label" for="pwa_icon" />
<field name="pwa_icon" widget="image" class="float-left oe_avatar"/>
</div>
</div>
</div>
</div>
</div>
</div>
</field>