🟢 Public

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)

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:

  1. Simple & Clear: Icon should be clear at small sizes
  2. High Contrast: High contrast for visibility
  3. No Text: Avoid text on small icons
  4. Consistent: Use the same style for all sizes

Verification

After generating icons:

  1. Check Files Exist:
    bash ls static/images/icons/

  2. Test in Browser:

  3. Open DevTools → Application → Manifest
  4. Check icons are loaded correctly

  5. Test Install:

  6. Try installing PWA
  7. 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

  1. Visit the website on mobile device
  2. Wait for install banner (or use browser menu)
  3. Tap "Install" or "Add to Home Screen"
  4. App will open in standalone mode

Offline Usage

  1. Visit pages while online (they will be cached)
  2. Go offline
  3. Previously visited pages will load from cache
  4. 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

  • preconnect untuk CDN (your-domain.com)
  • dns-prefetch untuk CDN
  • preload untuk 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

  • defer untuk Bootstrap JS
  • defer untuk 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