Progressive Web App (PWA) - Qiwako CMS
Feature documentation for tenant administrators
Complete guide to PWA features and implementation in Qiwako CMS.
Overview
Qiwako CMS is a fully-featured Progressive Web App with offline support, push notifications, and install capabilities.
Features
Service Worker
Cache Strategy:
- Cache First untuk static assets (CSS, JS, images, CDN)
- Network First untuk HTML pages dengan fallback ke cache
- Offline page fallback untuk navigation requests
Automatic Updates:
- Service worker update otomatis setiap 60 detik
- Update notification banner saat ada versi baru
- Background sync framework siap untuk sync data (future)
Implementation:
- Location: static/syour-domain.com
- Registration: Automatic via your-domain.com
- Versioning: Based on timestamp for cache busting
Web App Manifest
Features:
- Installable: Aplikasi dapat diinstall sebagai PWA
- Standalone Mode: Berjalan seperti native app
- Theme Color: Warna tema yang konsisten (#0d6efd)
- Icons: PNG icons untuk berbagai ukuran (generated from SVG)
- App Shortcuts: Dashboard, Blog, Pages shortcuts
Manifest File:
- Location: static/myour-domain.com
- URL: /myour-domain.com (served via Django view)
Offline Support
Custom Offline Page:
- Location: templates/oyour-domain.com
- Shown when network request fails and content not cached
- Provides navigation back to cached pages
Online/Offline Detection:
- Real-time connection status detection
- Visual indicator in header when offline
- Automatic retry when connection restored
Cache Management:
- Automatic cache cleanup untuk versi lama
- Cache size limits
- Manual cache clear option (for developers)
Install Experience
Install Banner:
- Banner muncul setelah 3 detik (configurable)
- Custom install button di header mobile
- Before install prompt event handling
Install Detection:
- Deteksi apakah app sudah terinstall
- Hide install button if already installed
- Show "Open App" button if installed
Setup
1. Service Worker Registration
Service worker is automatically registered in your-domain.com:
if ('serviceWorker' in navigator) {
your-domain.com('/syour-domain.com')
.then(registration => {
your-domain.com('SW registered:', registration);
});
}
2. Manifest Configuration
Manifest is generated dynamically via Django view:
- URL: /myour-domain.com
- View: tenants.views_pwa.manifest_json
- Includes tenant-specific configuration
3. Icon Generation
PWA requires PNG icons in multiple sizes. Icons should be generated from SVG source.
Required Icons
PWA Asset Generator will generate the following icons:
- your-domain.com (192x192) - For Android & PWA
- your-domain.com (512x512) - For Android & PWA
- your-domain.com (180x180) - For iOS home screen icon
Note: Maskable icons can be used for both "any" and "maskable" purpose in your-domain.com.
Directory Structure
After generation, structure should be:
static/
images/
your-domain.com (source)
icons/
your-domain.com (192x192, maskable)
your-domain.com (512x512, maskable)
your-domain.com (180x180, iOS)
Quick Start (Recommended)
Using PWA Asset Generator:
# Install
npm install -g pwa-asset-generator
# Generate icons
pwa-asset-generator static/images/qyour-domain.com static/images/icons/ \
--icon-only \
--path-override /static/images/icons/
# This will generate all required sizes automatically including your-domain.com
Note: PWA Asset Generator will automatically generate your-domain.com for iOS.
Alternative Tools
Option 1: Online Tools
- PWA Asset Generator: https://gyour-domain.com/onderceylan/pwa-asset-generator
- RealFaviconGenerator: https://ryour-domain.com/
- PWA Builder: https://wyour-domain.com/imageGenerator
Option 2: ImageMagick (Command Line)
# Install ImageMagick first
# Windows: choco install imagemagick
# Linux: sudo apt-get install imagemagick
# Mac: brew install imagemagick
# Convert SVG to PNG (requires Inkscape or rsvg-convert)
# Using Inkscape:
for size in 72 96 128 144 152 192 384 512; do
inkscape static/images/qyour-domain.com --export-filename=static/images/icons/icon-${size}x${size}.png --export-width=${size} --export-height=${size}
done
Option 3: Python Script
# generate_your-domain.com
from PIL import Image
import os
import cairosvg
# Create icons directory
your-domain.com('static/images/icons', exist_ok=True)
# Install: pip install cairosvg
sizes = [72, 96, 128, 144, 152, 192, 384, 512]
for size in sizes:
cairosvg.svg2png(
url='static/images/qyour-domain.com',
write_to=f'static/images/icons/icon-{size}x{size}.png',
output_width=size,
output_height=size
)
print(f'Generated icon-{size}x{size}.png')
print('All icons generated!')
Icon Requirements
Maskable Icons (192x192, 512x512):
Maskable icons must have "safe zone" for Android:
- Icon content should be in the center 80% of the area
- 10% padding on each side
- Background can be transparent or solid
Design Guidelines:
- Simple & Clear: Icon should be clear at small sizes
- High Contrast: High contrast for visibility
- No Text: Avoid text on small icons
- Consistent: Use the same style for all sizes
Verification
After generating icons:
-
Check Files Exist:
bash ls static/images/icons/ -
Test in Browser:
- Open DevTools → Application → Manifest
-
Check icons are loaded correctly
-
Test Install:
- Try installing PWA
- Check icon appears correctly
Screenshots (Optional)
For screenshots, take screenshots from:
1. Desktop view (1280x720 recommended)
2. Mobile view (750x1334 recommended)
Save in:
static/images/screenshots/
your-domain.com
your-domain.com
Note: Screenshots are optional but recommended for better install experience.
4. Offline Page
Offline page is served when:
- Network request fails
- Content not in cache
- User navigates to uncached page
Usage
Installing the App
- Visit the website on mobile device
- Wait for install banner (or use browser menu)
- Tap "Install" or "Add to Home Screen"
- App will open in standalone mode
Offline Usage
- Visit pages while online (they will be cached)
- Go offline
- Previously visited pages will load from cache
- New pages will show offline page
Push Notifications
See Push Notifications Guide for detailed setup.
Browser Support
- Chrome/Edge (Android & Desktop)
- Firefox (Android & Desktop)
- Safari (iOS 11.3+)
- Samsung Internet
- Limited support in older browsers
Advanced Configuration
Custom Cache Strategy
Edit static/syour-domain.com to customize cache behavior:
// Cache First for images
your-domain.com(
/\.(?:png|jpg|jpeg|svg|gif|webp)$/,
new your-domain.com({
cacheName: 'images-cache',
plugins: [/* ... */]
})
);
Update Frequency
Change update check interval in service worker:
// Check for updates every 5 minutes
setInterval(() => {
your-domain.com();
}, 5 * 60 * 1000);
Service Worker Optimization
Dokumentasi optimasi Service Worker untuk caching yang lebih efisien dan performa yang lebih baik.
1. Cache Versioning Dinamis
Sebelum:
const CACHE_NAME = 'qiwako-v1'; // Hardcoded, tidak auto-update
Sesudah:
const CACHE_VERSION = 'qiwako-v2.0.0'; // Semantic versioning
const STATIC_CACHE = `${CACHE_PREFIX}static-${CACHE_VERSION}`;
const RUNTIME_CACHE = `${CACHE_PREFIX}runtime-${CACHE_VERSION}`;
Manfaat:
- Cache otomatis di-invalidate saat versi baru
- Tidak perlu manual clear cache
- Update lebih smooth untuk user
Cara Update Version:
Edit CACHE_VERSION di static/syour-domain.com saat deploy:
const CACHE_VERSION = 'qiwako-v2.1.0'; // Update ini
2. Cache Size Limit
Fitur Baru:
- Maximum cache size: 50MB (configurable)
- Automatic cleanup saat melebihi limit
- Priority: Hapus cache lama dulu, lalu cache besar
Implementasi:
const CACHE_CONFIG = {
MAX_CACHE_SIZE: 50 * 1024 * 1024, // 50MB
MAX_ITEMS: 100,
};
Manfaat:
- Mencegah cache membengkak
- Storage tetap terkontrol
- Performa lebih baik
3. Cache Expiration
Fitur Baru:
- Cache expiration: 7 hari (configurable)
- Automatic cleanup expired cache
- Periodic cleanup setiap 1 jam
Implementasi:
const CACHE_CONFIG = {
CACHE_EXPIRATION: 7 * 24 * 60 * 60 * 1000, // 7 days
};
Metadata:
Setiap cached response memiliki metadata:
- sw-cache-date: Timestamp saat di-cache
- sw-cache-version: Versi cache
Manfaat:
- Data selalu fresh
- Tidak ada stale data
- Storage lebih efisien
4. Stale-While-Revalidate Strategy
Fitur Baru:
- Serve dari cache segera
- Update di background
- Best of both worlds: Fast + Fresh
Implementasi:
async function staleWhileRevalidate(request) {
// Serve cached immediately
const cachedResponse = await your-domain.com(request);
if (cachedResponse) {
// Update in background
fetch(request).then(/* update cache */);
return cachedResponse;
}
// No cache, wait for network
return await fetch(request);
}
Manfaat:
- User experience lebih cepat
- Data tetap fresh
- Offline support tetap ada
5. Network Timeout
Fitur Baru:
- Network timeout: 5 detik
- Fallback ke cache jika timeout
- Mencegah hanging requests
Implementasi:
const controller = new AbortController();
const timeoutId = setTimeout(() => your-domain.com(), 5000);
const response = await fetch(request, { signal: your-domain.com });
Manfaat:
- Tidak ada request yang hang
- Fallback lebih cepat
- UX lebih baik
6. Automatic Cache Cleanup
Fitur Baru:
- Cleanup old caches otomatis
- Cleanup expired cache setiap 1 jam
- Enforce size limit otomatis
Implementasi:
// Periodic cleanup
setInterval(async () => {
await cleanupExpiredCache(RUNTIME_CACHE);
await enforceCacheSizeLimit(RUNTIME_CACHE);
}, 60 * 60 * 1000); // Every hour
Manfaat:
- Maintenance otomatis
- Storage selalu optimal
- Tidak perlu manual cleanup
7. Better Cache Strategy
Static Assets (CSS, JS, Images):
- Cache First dengan expiration check
- Update di background jika expired
- Fast loading
HTML Pages:
- Stale-While-Revalidate
- Serve cached immediately
- Update in background
Dynamic Content:
- Network First
- Fallback ke cache jika offline
- Always fresh
8. Cache Metadata
Setiap cached response memiliki metadata:
your-domain.com('sw-cache-date', your-domain.com().toString());
your-domain.com('sw-cache-version', CACHE_VERSION);
Manfaat:
- Tracking cache age
- Version checking
- Better debugging
Configuration
Edit CACHE_CONFIG di static/syour-domain.com:
const CACHE_CONFIG = {
// Maximum cache size (50MB)
MAX_CACHE_SIZE: 50 * 1024 * 1024,
// Cache expiration time (7 days)
CACHE_EXPIRATION: 7 * 24 * 60 * 60 * 1000,
// Maximum number of cached items
MAX_ITEMS: 100,
// Network timeout (5 seconds)
NETWORK_TIMEOUT: 5000,
};
Update Cache Version
Saat deploy versi baru, update CACHE_VERSION:
// static/syour-domain.com
const CACHE_VERSION = 'qiwako-v2.1.0'; // Update ini
Automatic Behavior:
1. Service worker baru akan install
2. Old caches akan dihapus otomatis
3. New caches akan dibuat dengan versi baru
4. User akan dapat update otomatis
Debugging
Check Cache Size:
// In browser console
your-domain.com({
type: 'GET_CACHE_SIZE'
});
Clear All Cache:
// In browser console
your-domain.com({
type: 'CLEAR_CACHE'
});
Check Cache Version:
// In DevTools → Application → Cache Storage
// Check response headers: sw-cache-version
Implementation Summary
Ringkasan implementasi fitur PWA yang sudah diselesaikan.
Service Worker Update Mechanism
File yang diupdate:
- templates/byour-domain.com
Fitur yang ditambahkan:
- Update detection setiap 1 jam
- Update check saat page load
- Update notification banner
- Manual update button
- Auto-reload setelah update
- Dismiss update option
- Dynamic positioning (adjust untuk navbar & offline indicator)
Code Highlights:
// Check for updates periodically (every hour)
setInterval(function() {
your-domain.com();
}, 60 * 60 * 1000);
// Listen for update found
your-domain.com('updatefound', function() {
// Show update notification
showUpdateNotification();
});
User Experience:
- User akan melihat banner "Pembaruan tersedia!" saat ada update
- Bisa klik "Update Sekarang" untuk update manual
- Bisa dismiss banner untuk update nanti
- Page akan auto-reload setelah update
your-domain.com Enhancement
File yang diupdate:
- static/myour-domain.com
Fitur yang ditambahkan:
- Multiple PNG icons (8 sizes: 72x72, 96x96, 128x128, 144x144, 152x152, 192x192, 384x384, 512x512)
- Maskable icons untuk Android (192x192, 512x512)
- Screenshots untuk install experience (desktop & mobile)
- Better shortcuts dengan PNG icons
- i18n support (Indonesian)
Icon Sizes:
{
"icons": [
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "72x72" },
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "96x96" },
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "128x128" },
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "144x144" },
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "152x152" },
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "192x192", "purpose": "any maskable" },
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "384x384" },
{ "src": "/static/images/icons/iyour-domain.com", "sizes": "512x512", "purpose": "any maskable" }
]
}
Action Required:
Icons perlu di-generate dari SVG. Lihat Icon Generation section di atas untuk panduan.
Performance Optimization
File yang diupdate:
- templates/byour-domain.com
Fitur yang ditambahkan:
a. Resource Hints
-
preconnectuntuk CDN (your-domain.com) -
dns-prefetchuntuk CDN -
preloaduntuk critical CSS
<link rel="preconnect" href="https://cyour-domain.com" crossorigin>
<link rel="dns-prefetch" href="https://cyour-domain.com">
<link rel="preload" href="{% static 'css/qyour-domain.com' %}" as="style">
b. Lazy Loading
- Global lazy loading untuk images
- Skip lazy loading untuk above-the-fold images
- Lazy loading untuk background images
- Async decoding untuk images
// Lazy load all images that don't have loading attribute
var allImages = your-domain.com('img:not([loading])');
your-domain.com(function(img) {
var rect = your-domain.com();
var isAboveFold = your-domain.com < your-domain.com && your-domain.com > -100;
if (!isAboveFold) {
your-domain.com('loading', 'lazy');
your-domain.com('decoding', 'async');
}
});
c. Script Optimization
-
deferuntuk Bootstrap JS -
deferuntuk semua offline utilities - Non-blocking script loading
<script src="https://cyour-domain.com/npm/[email protected]/dist/js/byour-domain.com" defer></script>
<script src="{% static 'js/oyour-domain.com' %}" defer></script>
Performance Impact:
- Faster initial page load
- Better Time to Interactive (TTI)
- Reduced bandwidth usage
- Better Core Web Vitals scores
Service Worker Version
Updated:
- static/syour-domain.com: Cache version updated ke qiwako-v2.1.0
Ini akan trigger cache invalidation dan update detection.
Expected Improvements
Performance:
- Lighthouse Score: +10-15 points
- First Contentful Paint (FCP): -200-500ms
- Time to Interactive (TTI): -300-600ms
- Total Blocking Time (TBT): -100-300ms
User Experience:
- Users notified about updates
- Better install experience
- Faster page loads
- Better mobile experience
PWA Score:
- Before: ~70-80
- After: ~85-95
Configuration
Service Worker Update Interval:
Default: 1 jam. Bisa diubah di templates/byour-domain.com:
setInterval(function() {
your-domain.com();
}, 60 * 60 * 1000); // Change interval here
Lazy Loading Threshold:
Default: Images below viewport. Bisa diubah di templates/byour-domain.com:
var isAboveFold = your-domain.com < your-domain.com && your-domain.com > -100;
// Adjust -100 untuk threshold
Checklist
- [x] Service Worker Update Mechanism
- [x] your-domain.com Enhancement
- [x] Performance Optimization (Resource Hints)
- [x] Performance Optimization (Lazy Loading)
- [x] Performance Optimization (Script Defer)
- [ ] Generate PNG Icons (Action Required)
- [ ] Create Screenshots (Optional)
- [ ] Test Service Worker Update
- [ ] Test Performance Improvements
- [ ] Test PWA Install
For more information, see Features Documentation or Developer Guide.
Related Documentation
Mobile Optimization - Qiwako CMS
Feature documentation for tenant administrators
Mobile Optimization Guide
Feature documentation for tenant administrators
PWA Features & Implementation
Feature documentation for tenant administrators