Skip to content

Commit

Permalink
Included the default serviceworker, updated the manifest, Add the def…
Browse files Browse the repository at this point in the history
…ault offline page, updated the unit test, Redme
  • Loading branch information
silviolleite committed Nov 30, 2018
1 parent 73151d2 commit 79c6963
Show file tree
Hide file tree
Showing 25 changed files with 252 additions and 33 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,17 @@
- Add Oritentation on manifest.json
- Add tox
- Add Coverage


## 1.0.1

- Add django 2 requirement
- Use templateviews instead of own implementations
- Add content_types
- Update `README.md`
- Add `PWA_APP_FETCH_URL`
- Add default_config in `__init__.py`
- Add basic serviceworker
- Add default offline page and default icons
- Updated the unit tests

3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include *.md
recursive-include pwa/templates *
recursive-include pwa/templates *
recursive-include pwa/static *
75 changes: 71 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ All settings are optional, and the app will work fine with its internal defaults

Add the progressive web app URLs to urls.py:
```python
from django.conf.urls import url, include
from django.urls import path, include

urlpatterns = [
...
url('', include('pwa.urls')), # You MUST use an empty string as the URL prefix
path('', include('pwa.urls')), # You MUST use an empty string as the URL prefix
...
]
```
Expand All @@ -86,18 +86,85 @@ While running the Django test server:

1. Verify that `/manifest.json` is being served
1. Verify that `/serviceworker.js` is being served
1. Verify that `/offline` is being served
1. Use the Application tab in the Chrome Developer Tools to verify the progressive web app is configured correctly.
1. Use the "Add to homescreen" link on the Application Tab to verify you can add the app successfully.


The Service Worker
=====
By default, the service worker implemented by this app is:
```js
// Base Service Worker implementation. To use your own Service Worker, set the PWA_SERVICE_WORKER_PATH variable in settings.py

var staticCacheName = "django-pwa-v" + new Date().getTime();
var filesToCache = [
'/offline',
'/css/django-pwa-app.css',
'/images/icons/icon-72x72.png',
'/images/icons/icon-96x96.png',
'/images/icons/icon-128x128.png',
'/images/icons/icon-144x144.png',
'/images/icons/icon-152x152.png',
'/images/icons/icon-192x192.png',
'/images/icons/icon-384x384.png',
'/images/icons/icon-512x512.png',
];

// Cache on install
self.addEventListener("install", event => {
this.skipWaiting();
event.waitUntil(
caches.open(staticCacheName)
.then(cache => {
return cache.addAll(filesToCache);
})
)
});

// Clear cache on activate
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(cacheName => (cacheName.startsWith("django-pwa-")))
.filter(cacheName => (cacheName !== staticCacheName))
.map(cacheName => caches.delete(cacheName))
);
})
);
});

// Serve from Cache
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})
.catch(() => {
return caches.match('offline');
})
)
});
```

Adding Your Own Service Worker
=====
By default, the service worker implemented by this app is empty. To add service worker functionality, you'll want to create a `serviceworker.js` or similarly named file, and then point at it using the PWA_SERVICE_WORKER_PATH variable.
By default, the service worker implemented by this app is empty. To add service worker functionality, you'll want to create a `serviceworker.js` or similarly named template in a template directory, and then point at it using the PWA_SERVICE_WORKER_PATH variable (PWA_APP_FETCH_URL is passed through).

```python
PWA_SERVICE_WORKER_PATH = os.path.join(BASE_DIR, 'my_app', 'serviceworker.js')
PWA_SERVICE_WORKER_PATH = 'my_app/serviceworker.js'

```

The offline view
=====
By default, the offline view is implemented in `templates/offline.html`
You can overwrite it in a template directory if you continue using the default `serviceworker.js`.


Feedback
=====
I welcome your feedback and pull requests. Enjoy!
Expand Down
1 change: 1 addition & 0 deletions pwa/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = 'pwa.apps.PwaConfig'
36 changes: 32 additions & 4 deletions pwa/app_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import os

# Path to the service worker implementation. Default implementation is empty.
PWA_SERVICE_WORKER_PATH = getattr(settings, 'PWA_SERVICE_WORKER_PATH',
os.path.join(os.path.abspath(os.path.dirname(__file__)), 'templates', 'serviceworker.js'))
PWA_SERVICE_WORKER_PATH = getattr(settings, 'PWA_SERVICE_WORKER_PATH', 'serviceworker.js')

# App parameters to include in manifest.json and appropriate meta tags
PWA_APP_NAME = getattr(settings, 'PWA_APP_NAME', 'MyApp')
Expand All @@ -15,10 +14,39 @@
PWA_APP_DISPLAY = getattr(settings, 'PWA_APP_DISPLAY', 'standalone')
PWA_APP_ORIENTATION = getattr(settings, 'PWA_APP_ORIENTATION', 'any')
PWA_APP_START_URL = getattr(settings, 'PWA_APP_START_URL', '/')
PWA_APP_FETCH_URL = getattr(settings, 'PWA_APP_FETCH_URL', '/')
PWA_APP_ICONS = getattr(settings, 'PWA_APP_ICONS', [
{
'src': '/',
'sizes': '160x160'
'src': '/static/images/icons/icon-72x72.png',
'sizes': '72x72'
},
{
'src': '/static/images/icons/icon-96x96.png',
'sizes': '96x96'
},
{
'src': '/static/images/icons/icon-128x128.png',
'sizes': '128x128'
},
{
'src': '/static/images/icons/icon-144x144.png',
'sizes': '144x144'
},
{
'src': '/static/images/icons/icon-152x152.png',
'sizes': '152x152'
},
{
'src': '/static/images/icons/icon-192x192.png',
'sizes': '192x192'
},
{
'src': '/static/images/icons/icon-384x384.png',
'sizes': '384x384'
},
{
'src': '/static/images/icons/icon-512x512.png',
'sizes': '512x512'
}
])
PWA_APP_DIR = getattr(settings, 'PWA_APP_DIR', 'auto')
Expand Down
5 changes: 5 additions & 0 deletions pwa/static/css/django-pwa-app.css

Large diffs are not rendered by default.

Binary file added pwa/static/images/icons/Thumbs.db
Binary file not shown.
Binary file added pwa/static/images/icons/icon-128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa/static/images/icons/icon-144x144.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa/static/images/icons/icon-152x152.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa/static/images/icons/icon-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa/static/images/icons/icon-384x384.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa/static/images/icons/icon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa/static/images/icons/icon-72x72.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa/static/images/icons/icon-96x96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions pwa/templates/offline.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% load static %}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Default offline template</title>
<link href="{% static '/css/django-pwa-app.css' %}" rel="stylesheet">
</head>
<body>
<h1>You are currently not connected to any networks.</h1>
</body>
</html>
55 changes: 53 additions & 2 deletions pwa/templates/serviceworker.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,53 @@
// Empty Service Worker implementation. To use your own Service Worker, set the PWA_SERVICE_WORKER_PATH variable in
// settings.py
// Base Service Worker implementation. To use your own Service Worker, set the PWA_SERVICE_WORKER_PATH variable in settings.py

var staticCacheName = "django-pwa-v" + new Date().getTime();
var filesToCache = [
'/offline',
'/static/css/django-pwa-app.css',
'/static/images/icons/icon-72x72.png',
'/static/images/icons/icon-96x96.png',
'/static/images/icons/icon-128x128.png',
'/static/images/icons/icon-144x144.png',
'/static/images/icons/icon-152x152.png',
'/static/images/icons/icon-192x192.png',
'/static/images/icons/icon-384x384.png',
'/static/images/icons/icon-512x512.png',
];

// Cache on install
self.addEventListener("install", event => {
this.skipWaiting();
event.waitUntil(
caches.open(staticCacheName)
.then(cache => {
return cache.addAll(filesToCache);
})
)
});

// Clear cache on activate
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(cacheName => (cacheName.startsWith("django-pwa-")))
.filter(cacheName => (cacheName !== staticCacheName))
.map(cacheName => caches.delete(cacheName))
);
})
);
});

// Serve from Cache
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})
.catch(() => {
return caches.match('offline');
})
)
});
10 changes: 6 additions & 4 deletions pwa/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from django.conf.urls import url
from .views import manifest, service_worker
from django.urls import path

from .views import Manifest, ServiceWorker, OfflineView

# Serve up serviceworker.js and manifest.json at the root
urlpatterns = [
url('^serviceworker.js$', service_worker, name="serviceworker"),
url('^manifest.json$', manifest, name="manifest")
path('serviceworker.js', ServiceWorker.as_view(), name='serviceworker'),
path('manifest.json', Manifest.as_view(), name='manifest'),
path('offline', OfflineView.as_view(), name='offline')
]
32 changes: 21 additions & 11 deletions pwa/views.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
from django.http import HttpResponse
from django.shortcuts import render
from django.views.generic.base import TemplateView

from . import app_settings


def service_worker(request):
response = HttpResponse(open(app_settings.PWA_SERVICE_WORKER_PATH).read(), content_type='application/javascript')
return response
class ServiceWorker(TemplateView):
content_type = 'application/javascript'
template_name = app_settings.PWA_SERVICE_WORKER_PATH

def get_context_data(self, **kwargs):
kwargs['PWA_APP_FETCH_URL'] = app_settings.PWA_APP_FETCH_URL
return super().get_context_data(**kwargs)


def manifest(request):
return render(request, 'manifest.json', {
setting_name: getattr(app_settings, setting_name)
for setting_name in dir(app_settings)
if setting_name.startswith('PWA_')
})
class Manifest(TemplateView):
content_type = 'application/json'
template_name = 'manifest.json'

def get_context_data(self, **kwargs):
for setting_name in dir(app_settings):
if setting_name.startswith('PWA_'):
kwargs[setting_name] = getattr(app_settings, setting_name)
return super().get_context_data(**kwargs)


class OfflineView(TemplateView):
template_name = "offline.html"
12 changes: 9 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@
import pypandoc

long_description = pypandoc.convert('README.md', 'rst')
except:
except RuntimeError:
long_description = short_description

# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

install_requirements = [
"django>=2",
]

setup(
name='django-pwa',
version='1.0.0',
version='1.0.1',
packages=find_packages(),
install_requires=install_requirements,
include_package_data=True,
license='MIT License',
description=short_description,
Expand All @@ -30,7 +35,8 @@
classifiers=[
'Environment :: Web Environment',
'Framework :: Django',
'Framework :: Django :: 1.10',
'Framework :: Django :: 2.0',
'Framework :: Django :: 2.1',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
Expand Down
4 changes: 3 additions & 1 deletion tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase',
}
}
}

STATIC_URL = '/static/'
1 change: 1 addition & 0 deletions tests/test_settings_attr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def test_has_defined(self):
'PWA_APP_DISPLAY',
'PWA_APP_ORIENTATION',
'PWA_APP_START_URL',
'PWA_APP_FETCH_URL',
'PWA_APP_ICONS',
'PWA_APP_DIR',
'PWA_APP_LANG'
Expand Down
9 changes: 8 additions & 1 deletion tests/test_template_tag_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ def setUp(self):
def test_has_tags(self):
"""Must contains the tags in HTML"""
tags = [
'<link rel="apple-touch-icon" href="/" sizes="160x160">',
'<link rel="apple-touch-icon" href="/images/icons/icon-72x72.png" sizes="72x72">',
'<link rel="apple-touch-icon" href="/images/icons/icon-96x96.png" sizes="96x96">',
'<link rel="apple-touch-icon" href="/images/icons/icon-128x128.png" sizes="128x128">',
'<link rel="apple-touch-icon" href="/images/icons/icon-144x144.png" sizes="144x144">',
'<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png" sizes="152x152">',
'<link rel="apple-touch-icon" href="/images/icons/icon-192x192.png" sizes="192x192">',
'<link rel="apple-touch-icon" href="/images/icons/icon-384x384.png" sizes="384x384">',
'<link rel="apple-touch-icon" href="/images/icons/icon-512x512.png" sizes="512x512">',
'<link rel="manifest" href="/manifest.json">',
'<meta name="theme-color" content="#000">',
'<meta name="apple-mobile-web-app-capable" content="yes">',
Expand Down
10 changes: 10 additions & 0 deletions tests/test_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,13 @@ def test_manifest_contains(self):
for expected in contents:
with self.subTest():
self.assertContains(self.response, expected)


class OfflineTest(TestCase):
def setUp(self):
self.response = self.client.get(r('offline'))

def test_get(self):
"""GET /offline Should return status code 200"""
self.assertEqual(200, self.response.status_code)

Loading

0 comments on commit 79c6963

Please sign in to comment.