Add PWA support and fix PO BOX display in address
- Add comprehensive PWA configuration with manifest, service worker, and meta tags - Replace Android APK download with cross-platform PWA installation - Fix church address to include PO BOX information from API - Update contact page and footer to properly display multi-line addresses - Enable native app-like experience on all platforms without app store requirements
This commit is contained in:
parent
5f6430a4ee
commit
16caf6c3c4
32
astro-church-website/public/icons/app-icon.svg
Normal file
32
astro-church-website/public/icons/app-icon.svg
Normal file
|
@ -0,0 +1,32 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<defs>
|
||||
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#d4af37" />
|
||||
<stop offset="100%" stop-color="#b8941f" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background circle -->
|
||||
<circle cx="256" cy="256" r="240" fill="#1a1a1a" stroke="url(#gradient)" stroke-width="8"/>
|
||||
|
||||
<!-- Church building -->
|
||||
<rect x="180" y="300" width="152" height="120" fill="url(#gradient)" rx="4"/>
|
||||
|
||||
<!-- Church roof -->
|
||||
<polygon points="256,200 160,280 352,280" fill="#d4af37"/>
|
||||
|
||||
<!-- Cross on top -->
|
||||
<rect x="248" y="160" width="16" height="60" fill="#fff" rx="2"/>
|
||||
<rect x="234" y="174" width="44" height="16" fill="#fff" rx="2"/>
|
||||
|
||||
<!-- Door -->
|
||||
<rect x="236" y="340" width="40" height="80" fill="#1a1a1a" rx="20"/>
|
||||
|
||||
<!-- Windows -->
|
||||
<rect x="200" y="320" width="24" height="32" fill="#1a1a1a" rx="4"/>
|
||||
<rect x="288" y="320" width="24" height="32" fill="#1a1a1a" rx="4"/>
|
||||
|
||||
<!-- Bell tower -->
|
||||
<rect x="230" y="220" width="52" height="80" fill="url(#gradient)" rx="4"/>
|
||||
<rect x="238" y="210" width="36" height="20" fill="#d4af37" rx="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
63
astro-church-website/public/manifest.json
Normal file
63
astro-church-website/public/manifest.json
Normal file
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"name": "Rockville Tolland SDA Church",
|
||||
"short_name": "RTSDA Church",
|
||||
"description": "Official website and app for Rockville Tolland Seventh-day Adventist Church",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#1a1a1a",
|
||||
"theme_color": "#d4af37",
|
||||
"orientation": "portrait-primary",
|
||||
"categories": ["religion", "lifestyle", "education"],
|
||||
"lang": "en-US",
|
||||
"scope": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "72x72",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "96x96",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "128x128",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "144x144",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "152x152",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "192x192",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "384x384",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "512x512",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
43
astro-church-website/public/sw.js
Normal file
43
astro-church-website/public/sw.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
const CACHE_NAME = 'rtsda-church-v1';
|
||||
const urlsToCache = [
|
||||
'/',
|
||||
'/about',
|
||||
'/contact',
|
||||
'/sermons',
|
||||
'/events',
|
||||
'/live',
|
||||
'/manifest.json'
|
||||
];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then((cache) => {
|
||||
return cache.addAll(urlsToCache);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then((response) => {
|
||||
// Return cached version or fetch from network
|
||||
return response || fetch(event.request);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => {
|
||||
if (cacheName !== CACHE_NAME) {
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
|
@ -149,7 +149,11 @@ const currentYear = new Date().getFullYear();
|
|||
{address && (
|
||||
<div class="flex items-start space-x-2 text-sm text-gray-300">
|
||||
<i data-lucide="map-pin" class="w-4 h-4 text-gold-400 mt-0.5 flex-shrink-0"></i>
|
||||
<span>{address}</span>
|
||||
<div class="leading-relaxed">
|
||||
{address.split('\n').map(line => (
|
||||
<div class="mb-1 last:mb-0">{line}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{phone && (
|
||||
|
@ -222,17 +226,15 @@ const currentYear = new Date().getFullYear();
|
|||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Android APK Download -->
|
||||
<button onclick="downloadApk()" class="app-download-btn w-full text-left">
|
||||
<!-- PWA Installation -->
|
||||
<button onclick="installPWA()" class="app-download-btn w-full text-left">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-8 h-8 bg-gradient-to-br from-primary-600 to-primary-700 rounded-lg flex items-center justify-center">
|
||||
<svg viewBox="0 0 24 24" class="w-4 h-4 fill-gold-400">
|
||||
<path d="M17.6 9.48l1.84-3.18c.16-.31.04-.69-.26-.85a.637.637 0 0 0-.83.22l-1.88 3.24a11.463 11.463 0 0 0-8.94 0L5.65 5.67a.643.643 0 0 0-.87-.2c-.28.18-.37.54-.22.83L6.4 9.48A10.78 10.78 0 0 0 1 18h22a10.78 10.78 0 0 0-5.4-8.52zM7 15.25a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5zm10 0a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5z"/>
|
||||
</svg>
|
||||
<i data-lucide="smartphone" class="w-4 h-4 text-gold-400"></i>
|
||||
</div>
|
||||
<div class="text-left">
|
||||
<div class="text-xs text-gray-400">Download APK</div>
|
||||
<div class="text-sm font-medium text-white">Android</div>
|
||||
<div class="text-xs text-gray-400">Install as App</div>
|
||||
<div class="text-sm font-medium text-white">Mobile & Desktop</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -279,8 +281,71 @@ const currentYear = new Date().getFullYear();
|
|||
</style>
|
||||
|
||||
<script is:inline>
|
||||
// Make downloadApk function globally available
|
||||
window.downloadApk = function() {
|
||||
window.location.href = 'https://api.rockvilletollandsda.church/uploads/rtsda_android/current';
|
||||
let deferredPrompt = null;
|
||||
|
||||
// Listen for the beforeinstallprompt event
|
||||
window.addEventListener('beforeinstallprompt', (e) => {
|
||||
console.log('beforeinstallprompt event fired');
|
||||
e.preventDefault();
|
||||
deferredPrompt = e;
|
||||
});
|
||||
|
||||
// PWA installation handler
|
||||
window.installPWA = function() {
|
||||
if (deferredPrompt) {
|
||||
// Show the install prompt
|
||||
deferredPrompt.prompt();
|
||||
deferredPrompt.userChoice.then((choiceResult) => {
|
||||
if (choiceResult.outcome === 'accepted') {
|
||||
console.log('User accepted the install prompt');
|
||||
} else {
|
||||
console.log('User dismissed the install prompt');
|
||||
}
|
||||
deferredPrompt = null;
|
||||
});
|
||||
} else {
|
||||
// Show manual instructions for different platforms
|
||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
|
||||
let instructions = '';
|
||||
|
||||
if (isIOS) {
|
||||
instructions = `To install this app on iOS:
|
||||
|
||||
1. Tap the Share button (□↗) in Safari
|
||||
2. Scroll down and tap "Add to Home Screen"
|
||||
3. Tap "Add" to confirm
|
||||
4. The app will appear on your home screen
|
||||
|
||||
This creates a full-screen app experience!`;
|
||||
} else if (isMobile) {
|
||||
instructions = `To install this app on Android:
|
||||
|
||||
1. Open this website in Chrome browser
|
||||
2. Tap the menu (⋮) in the top right
|
||||
3. Select "Add to Home screen" or "Install app"
|
||||
4. Follow the prompts to install
|
||||
5. The app will appear on your home screen
|
||||
|
||||
This creates a full-screen app experience!`;
|
||||
} else {
|
||||
instructions = `To install this app on Desktop:
|
||||
|
||||
Chrome/Edge:
|
||||
1. Look for the install icon (⊕) in the address bar
|
||||
2. Click it and select "Install"
|
||||
3. The app will open in its own window
|
||||
|
||||
Firefox/Safari:
|
||||
1. Click the menu button
|
||||
2. Look for "Install" or "Add to Home Screen" option
|
||||
3. Follow the prompts to install
|
||||
|
||||
This creates a dedicated app window without browser tabs!`;
|
||||
}
|
||||
|
||||
alert(instructions);
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -16,6 +16,29 @@ const { title, description = 'Proclaiming the Three Angels\' Message with love a
|
|||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>{title}</title>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="theme-color" content="#d4af37" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="RTSDA Church" />
|
||||
<meta name="application-name" content="RTSDA Church" />
|
||||
<meta name="msapplication-TileColor" content="#d4af37" />
|
||||
|
||||
<!-- Web App Manifest -->
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/favicon.svg" />
|
||||
|
||||
<!-- Preload Critical Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
|
@ -117,6 +140,19 @@ const { title, description = 'Proclaiming the Three Angels\' Message with love a
|
|||
document.querySelectorAll('[data-animate]').forEach(el => {
|
||||
observer.observe(el);
|
||||
});
|
||||
|
||||
// Register Service Worker for PWA
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
.then((registration) => {
|
||||
console.log('SW registered: ', registration);
|
||||
})
|
||||
.catch((registrationError) => {
|
||||
console.log('SW registration failed: ', registrationError);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -18,6 +18,7 @@ try {
|
|||
address = getChurchAddress();
|
||||
phone = getContactPhone();
|
||||
|
||||
|
||||
// Get the base email from church-core and make it dynamic based on current domain
|
||||
const baseEmail = getContactEmail();
|
||||
const currentUrl = Astro.url.hostname;
|
||||
|
@ -152,7 +153,11 @@ try {
|
|||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-white mb-1">Visit Us</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300">{address}</p>
|
||||
<div class="text-gray-600 dark:text-gray-300">
|
||||
{address.split('\n').map(line => (
|
||||
<p class="mb-1 last:mb-0">{line}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -893,11 +893,31 @@ pub fn get_donation_url() -> String {
|
|||
.unwrap_or_else(|| "https://adventistgiving.org/donate/ANRTOL".to_string())
|
||||
}
|
||||
|
||||
/// Get church address with fallback
|
||||
/// Get church address with fallback - includes PO BOX if available
|
||||
pub fn get_church_address() -> String {
|
||||
get_cached_config()
|
||||
.and_then(|config| config.church_address)
|
||||
.unwrap_or_else(|| "115 Snipsic Lake Road, Tolland, CT 06084".to_string())
|
||||
// Get the raw config JSON and parse it directly
|
||||
let config_json = fetch_config_json();
|
||||
|
||||
if let Ok(config_value) = serde_json::from_str::<serde_json::Value>(&config_json) {
|
||||
let mut address_lines = Vec::new();
|
||||
|
||||
// Add physical address if available
|
||||
if let Some(church_address) = config_value.get("church_address").and_then(|v| v.as_str()) {
|
||||
address_lines.push(church_address.to_string());
|
||||
}
|
||||
|
||||
// Add PO BOX as second line if available
|
||||
if let Some(po_box) = config_value.get("po_box").and_then(|v| v.as_str()) {
|
||||
address_lines.push(po_box.to_string());
|
||||
}
|
||||
|
||||
if !address_lines.is_empty() {
|
||||
return address_lines.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if no config available
|
||||
"9 Hartford Tpke Tolland CT 06084".to_string()
|
||||
}
|
||||
|
||||
/// Get coordinates from config coordinates object or return empty vector
|
||||
|
|
Loading…
Reference in a new issue