fix: PWA install banner und Icons
- Dockerfile: icon-192.png und icon-512.png in Container aufgenommen - manifest.json: favicon.ico entfernt (wurde als text/html geliefert) - manifest.json: Cache-Busting ?v=2 für alle Icons - index.html: Logo-Bild auf ?v=2 aktualisiert - extra8002.conf: manifest.json Content-Type auf application/manifest+json gesetzt - Alle Sub-App manifest.json: scope von '/' auf jeweiligen Pfad korrigiert (/nachsuche/, /drohnenfuehrer/, /stoeberhunde/) - icon-192.png und icon-512.png: aus logo-fallingbostel.png generiert
This commit is contained in:
parent
5eb14a7826
commit
52f21d964c
|
|
@ -20,9 +20,9 @@ services:
|
|||
build: ./backend
|
||||
container_name: drohnenfuehrer-backend
|
||||
restart: unless-stopped
|
||||
# Port 5001 only bound to localhost
|
||||
# Port 5011 only bound to localhost
|
||||
ports:
|
||||
- "127.0.0.1:5001:5000"
|
||||
- "127.0.0.1:5011:5000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- MONGO_URI=mongodb://drohnenfuehrer:${MONGO_PASSWORD}@mongo:27017/drohnenfuehrer?authSource=admin
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"start_url": "/drohnenfuehrer/",
|
||||
"scope": "/drohnenfuehrer/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"theme_color": "#2d6a2d",
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ services:
|
|||
build: ./backend
|
||||
container_name: nachsuche-backend
|
||||
restart: unless-stopped
|
||||
# Port 5000 only bound to localhost - accessible by host nginx, NOT from external IPs
|
||||
# Port 5010 only bound to localhost - accessible by host nginx, NOT from external IPs
|
||||
ports:
|
||||
- "127.0.0.1:5000:5000"
|
||||
- "127.0.0.1:5010:5000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- MONGO_URI=mongodb://nachsuche:${MONGO_PASSWORD}@mongo:27017/nachsuche?authSource=admin
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"start_url": "/nachsuche/",
|
||||
"scope": "/nachsuche/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"theme_color": "#2d6a2d",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ FROM nginx:alpine
|
|||
COPY extra8002.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Portal-Seiten (statischer Webroot)
|
||||
COPY index.html manifest.json sw.js offline.html /usr/share/nginx/html/portal/
|
||||
COPY index.html manifest.json sw.js offline.html icon-192.png icon-512.png /usr/share/nginx/html/portal/
|
||||
|
||||
EXPOSE 80
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ services:
|
|||
container_name: jagd-portal
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8090:80"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ server {
|
|||
|
||||
location = /manifest.json {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Content-Type "application/manifest+json";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ server {
|
|||
# Nachsuche (/nachsuche/)
|
||||
# ──────────────────────────────────────────────
|
||||
location /nachsuche/api/ {
|
||||
proxy_pass http://host.docker.internal:5000/api/;
|
||||
proxy_pass http://host.docker.internal:5010/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
|
@ -49,7 +50,7 @@ server {
|
|||
# Drohnenführer (/drohnenfuehrer/)
|
||||
# ──────────────────────────────────────────────
|
||||
location /drohnenfuehrer/api/ {
|
||||
proxy_pass http://host.docker.internal:5001/api/;
|
||||
proxy_pass http://host.docker.internal:5011/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
|
@ -74,7 +75,7 @@ server {
|
|||
# Stöberhunde (/stoeberhunde/)
|
||||
# ──────────────────────────────────────────────
|
||||
location /stoeberhunde/api/ {
|
||||
proxy_pass http://host.docker.internal:5002/api/;
|
||||
proxy_pass http://host.docker.internal:5012/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
|
|
@ -11,13 +11,17 @@
|
|||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="description" content="Jagd Apps Heidekreis – Nachsuche, Drohnenführer, Stöberhunde" />
|
||||
<title>Jagd Apps Heidekreis</title>
|
||||
<script>
|
||||
window.__installPrompt = null;
|
||||
window.addEventListener('beforeinstallprompt', function(e) {
|
||||
e.preventDefault();
|
||||
window.__installPrompt = e;
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
|
||||
:root {
|
||||
--green: #2d6a2d;
|
||||
--green-dark: #1e4d1e;
|
||||
--green-light: #e8f5e9;
|
||||
--text: #1a1a1a;
|
||||
--muted: #555;
|
||||
--bg: #f5f5f0;
|
||||
|
|
@ -26,158 +30,38 @@
|
|||
--shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
--radius: 12px;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
header {
|
||||
background: var(--green);
|
||||
color: #fff;
|
||||
padding: 1.25rem 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 1.35rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
header p {
|
||||
margin: 0.3rem 0 0;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 520px;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem 1rem 3rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.app-card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-left: 5px solid var(--green);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.25rem;
|
||||
padding: 1.25rem 1.25rem;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
transition: transform 0.15s, box-shadow 0.15s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.app-card:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.app-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 18px rgba(0,0,0,0.12);
|
||||
}
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 2.4rem;
|
||||
flex-shrink: 0;
|
||||
width: 52px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.2rem;
|
||||
}
|
||||
|
||||
.app-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.app-arrow {
|
||||
color: var(--green);
|
||||
font-size: 1.3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* Install banner */
|
||||
#install-banner {
|
||||
display: none;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--green);
|
||||
color: #fff;
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.88rem;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100vh; }
|
||||
header { background: var(--green); color: #fff; padding: 1.25rem 1.5rem; text-align: center; }
|
||||
header h1 { margin: 0; font-size: 1.35rem; font-weight: 700; display: flex; align-items: center; justify-content: center; gap: 0.6rem; }
|
||||
header h1 img { height: 2.2rem; width: auto; vertical-align: middle; }
|
||||
header p { margin: 0.3rem 0 0; font-size: 0.9rem; opacity: 0.85; }
|
||||
main { max-width: 520px; margin: 0 auto; padding: 1.5rem 1rem 3rem; display: flex; flex-direction: column; gap: 1rem; }
|
||||
.app-card { background: var(--card-bg); border: 1px solid var(--border); border-left: 5px solid var(--green); border-radius: var(--radius); box-shadow: var(--shadow); display: flex; align-items: center; gap: 1.25rem; padding: 1.25rem; text-decoration: none; color: var(--text); transition: transform 0.15s, box-shadow 0.15s; -webkit-tap-highlight-color: transparent; }
|
||||
.app-card:active { transform: scale(0.98); }
|
||||
@media (hover: hover) { .app-card:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(0,0,0,0.12); } }
|
||||
.app-icon { font-size: 2.4rem; flex-shrink: 0; width: 52px; text-align: center; }
|
||||
.app-info { flex: 1; }
|
||||
.app-name { font-size: 1.1rem; font-weight: 700; margin: 0 0 0.2rem; }
|
||||
.app-desc { font-size: 0.85rem; color: var(--muted); margin: 0; line-height: 1.4; }
|
||||
.app-arrow { color: var(--green); font-size: 1.3rem; flex-shrink: 0; }
|
||||
footer { text-align: center; padding: 1rem; font-size: 0.75rem; color: var(--muted); }
|
||||
#install-banner { display: none; position: sticky; top: 0; z-index: 100; background: var(--green); color: #fff; padding: 0.6rem 1rem; font-size: 0.88rem; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
|
||||
#install-banner.visible { display: flex; }
|
||||
#install-banner span { flex: 1; }
|
||||
#install-btn {
|
||||
background: #fff;
|
||||
color: var(--green);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0.3rem 0.8rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#dismiss-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.8);
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
padding: 0.2rem 0.3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
#install-btn { background: #fff; color: var(--green); border: none; border-radius: 4px; padding: 0.3rem 0.8rem; font-weight: 600; font-size: 0.85rem; cursor: pointer; white-space: nowrap; }
|
||||
#dismiss-btn { background: transparent; border: none; color: rgba(255,255,255,0.8); font-size: 1rem; cursor: pointer; padding: 0.2rem 0.3rem; flex-shrink: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="install-banner" role="banner">
|
||||
<span id="install-text">📲 App auf dem Homescreen installieren</span>
|
||||
<button id="install-btn">Installieren</button>
|
||||
<button id="install-btn" style="display:none">Installieren</button>
|
||||
<button id="dismiss-btn" aria-label="Schließen">✕</button>
|
||||
</div>
|
||||
|
||||
<header>
|
||||
<h1>🦌 Jagd Apps Heidekreis</h1>
|
||||
<h1><img src="/icon-192.png?v=2" alt="Logo"> Jagd Apps Heidekreis</h1>
|
||||
<p>Wählen Sie Ihre App</p>
|
||||
</header>
|
||||
|
||||
|
|
@ -190,7 +74,6 @@
|
|||
</div>
|
||||
<span class="app-arrow">›</span>
|
||||
</a>
|
||||
|
||||
<a href="/drohnenfuehrer/" class="app-card">
|
||||
<div class="app-icon">🚁</div>
|
||||
<div class="app-info">
|
||||
|
|
@ -199,7 +82,6 @@
|
|||
</div>
|
||||
<span class="app-arrow">›</span>
|
||||
</a>
|
||||
|
||||
<a href="/stoeberhunde/" class="app-card">
|
||||
<div class="app-icon">🐾</div>
|
||||
<div class="app-info">
|
||||
|
|
@ -213,51 +95,63 @@
|
|||
<footer>Jägerschaft Fallingbostel e.V.</footer>
|
||||
|
||||
<script>
|
||||
// Register service worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
||||
}
|
||||
|
||||
// PWA install banner
|
||||
const banner = document.getElementById('install-banner');
|
||||
const banner = document.getElementById('install-banner');
|
||||
const installBtn = document.getElementById('install-btn');
|
||||
const dismissBtn = document.getElementById('dismiss-btn');
|
||||
const installText = document.getElementById('install-text');
|
||||
const DISMISSED_KEY = 'portal-pwa-dismissed';
|
||||
|
||||
let deferredPrompt = null;
|
||||
|
||||
const isInStandalone = () =>
|
||||
const isStandalone = () =>
|
||||
window.matchMedia('(display-mode: standalone)').matches ||
|
||||
window.navigator.standalone === true;
|
||||
|
||||
const isIOS = () =>
|
||||
/iphone|ipad|ipod/i.test(navigator.userAgent) && !window.MSStream;
|
||||
|
||||
if (!isInStandalone() && !localStorage.getItem(DISMISSED_KEY)) {
|
||||
const isSamsung = () =>
|
||||
/SamsungBrowser/i.test(navigator.userAgent);
|
||||
|
||||
function attachInstall(prompt) {
|
||||
installBtn.style.display = '';
|
||||
installBtn.onclick = async () => {
|
||||
prompt.prompt();
|
||||
const { outcome } = await prompt.userChoice;
|
||||
if (outcome === 'accepted') banner.classList.remove('visible');
|
||||
};
|
||||
}
|
||||
|
||||
if (!isStandalone() && !sessionStorage.getItem('portal-pwa-dismissed')) {
|
||||
if (isIOS()) {
|
||||
installText.textContent = '📲 Zum Homescreen hinzufügen: Teilen ⬆️ → „Zum Home-Bildschirm"';
|
||||
installBtn.style.display = 'none';
|
||||
installText.textContent = '📲 Zum Homescreen: Teilen ⬆️ → „Zum Home-Bildschirm"';
|
||||
banner.classList.add('visible');
|
||||
} else if (isSamsung()) {
|
||||
installText.textContent = '📲 App installieren: Menü ⋮ → „Seite hinzufügen" → „Auf Startbildschirm"';
|
||||
banner.classList.add('visible');
|
||||
if (window.__installPrompt) {
|
||||
attachInstall(window.__installPrompt);
|
||||
} else {
|
||||
window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); attachInstall(e); });
|
||||
}
|
||||
} else {
|
||||
window.addEventListener('beforeinstallprompt', (e) => {
|
||||
const activate = (e) => {
|
||||
e.preventDefault();
|
||||
deferredPrompt = e;
|
||||
installText.textContent = '📲 App auf dem Homescreen installieren';
|
||||
attachInstall(e);
|
||||
banner.classList.add('visible');
|
||||
});
|
||||
};
|
||||
if (window.__installPrompt) {
|
||||
activate(window.__installPrompt);
|
||||
} else {
|
||||
window.addEventListener('beforeinstallprompt', activate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
installBtn.addEventListener('click', async () => {
|
||||
if (!deferredPrompt) return;
|
||||
deferredPrompt.prompt();
|
||||
const { outcome } = await deferredPrompt.userChoice;
|
||||
if (outcome === 'accepted') banner.classList.remove('visible');
|
||||
deferredPrompt = null;
|
||||
});
|
||||
|
||||
dismissBtn.addEventListener('click', () => {
|
||||
localStorage.setItem(DISMISSED_KEY, '1');
|
||||
sessionStorage.setItem('portal-pwa-dismissed', '1');
|
||||
banner.classList.remove('visible');
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,28 @@
|
|||
"name": "Jagd Apps Heidekreis",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
"src": "icon-192.png?v=2",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "icon-192.png?v=2",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "icon-512.png?v=2",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "icon-512.png?v=2",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Service Worker: Offline-Fallback für Portal-Selektor
|
||||
const CACHE_NAME = 'portal-offline-v1';
|
||||
const CACHE_NAME = 'portal-offline-v2';
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ services:
|
|||
build: ./backend
|
||||
container_name: stoeberhunde-backend
|
||||
restart: unless-stopped
|
||||
# Port 5002 only bound to localhost
|
||||
# Port 5012 only bound to localhost
|
||||
ports:
|
||||
- "127.0.0.1:5002:5000"
|
||||
- "127.0.0.1:5012:5000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- MONGO_URI=mongodb://stoeberhunde:${MONGO_PASSWORD}@mongo:27017/stoeberhunde?authSource=admin
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"start_url": "/stoeberhunde/",
|
||||
"scope": "/stoeberhunde/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"theme_color": "#2d6a2d",
|
||||
|
|
|
|||
Loading…
Reference in New Issue