diff --git a/config/urls.py b/config/urls.py index d61560b..574afd1 100644 --- a/config/urls.py +++ b/config/urls.py @@ -6,18 +6,24 @@ from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static +from django.views.generic import TemplateView from users.urls import api_urlpatterns as users_api_urls, web_urlpatterns as users_web_urls from tasks.urls import api_urlpatterns as tasks_api_urls, web_urlpatterns as tasks_web_urls from sync.urls import api_urlpatterns as sync_api_urls from notifications.urls import api_urlpatterns as notifications_api_urls from tasks.views_debug import debug_user_agent -from .views import api_root +from .views import api_root, manifest_webmanifest, service_worker urlpatterns = [ # Admin path('admin/', admin.site.urls), + # PWA + path('manifest.webmanifest', manifest_webmanifest, name='manifest'), + path('sw.js', service_worker, name='service-worker'), + path('offline/', TemplateView.as_view(template_name='offline.html'), name='offline'), + # Debug endpoint (remove in production) path('debug/user-agent/', debug_user_agent, name='debug-user-agent'), diff --git a/config/views.py b/config/views.py index 47c2a8b..5be4937 100644 --- a/config/views.py +++ b/config/views.py @@ -3,6 +3,23 @@ API root views for KeepItGoing. """ from django.http import JsonResponse +from django.shortcuts import render + + +def manifest_webmanifest(request): + """Serve the PWA web app manifest, rendered so icon URLs pick up static hashing.""" + return render(request, 'manifest.webmanifest', content_type='application/manifest+json') + + +def service_worker(request): + """ + Serve the service worker script from the site root so its default scope + covers the whole app (a service worker can only control paths at or + below where it's served from). + """ + response = render(request, 'sw.js', content_type='application/javascript') + response['Cache-Control'] = 'no-cache' + return response def api_root(request): diff --git a/static/favicons/apple-touch-icon-180.png b/static/favicons/apple-touch-icon-180.png new file mode 100644 index 0000000..18482ba Binary files /dev/null and b/static/favicons/apple-touch-icon-180.png differ diff --git a/static/favicons/icon-192.png b/static/favicons/icon-192.png new file mode 100644 index 0000000..582da33 Binary files /dev/null and b/static/favicons/icon-192.png differ diff --git a/static/favicons/icon-512-maskable.png b/static/favicons/icon-512-maskable.png new file mode 100644 index 0000000..ac8aef9 Binary files /dev/null and b/static/favicons/icon-512-maskable.png differ diff --git a/static/favicons/icon-512.png b/static/favicons/icon-512.png new file mode 100644 index 0000000..689f9b7 Binary files /dev/null and b/static/favicons/icon-512.png differ diff --git a/static/js/app.js b/static/js/app.js index b05984a..7d13d2d 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -7,8 +7,19 @@ document.addEventListener('DOMContentLoaded', function() { initTheme(); initTaskSelection(); initTimerDisplays(); + registerServiceWorker(); }); +/* ============================================ + Service Worker + ============================================ */ + +function registerServiceWorker() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/sw.js'); + } +} + /* ============================================ Theme Toggle ============================================ */ diff --git a/templates/base.html b/templates/base.html index e999d8b..7a7c838 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,6 +9,13 @@ + + + + + + + {% block extra_css %}{% endblock %} diff --git a/templates/manifest.webmanifest b/templates/manifest.webmanifest new file mode 100644 index 0000000..ad5da74 --- /dev/null +++ b/templates/manifest.webmanifest @@ -0,0 +1,26 @@ +{% load static %}{ + "name": "KeepItGoing", + "short_name": "KeepItGoing", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#3B82F6", + "icons": [ + { + "src": "{% static 'favicons/icon-192.png' %}", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "{% static 'favicons/icon-512.png' %}", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "{% static 'favicons/icon-512-maskable.png' %}", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/templates/offline.html b/templates/offline.html new file mode 100644 index 0000000..b248d11 --- /dev/null +++ b/templates/offline.html @@ -0,0 +1,19 @@ +{% extends 'base.html' %} + +{% block title %}You're Offline - KeepItGoing{% endblock %} + +{% block content %} +
You're offline
+This page isn't available without a connection. Reconnect and try again.
+ +You're offline
+This page isn't available without a connection. Reconnect and try again.
+ +