Ban Yaro Blues — Hymne in der WELT-Welt
Eigener Song (KI-Demo via Suno) als Marken-Hymne. Dezente Player-Karte unter dem Tageszitat; preload=none → 6 MB MP3 lädt erst bei Play, der SW cacht sie danach für offline. Der Banner ist einmalige Einladung und verschwindet nach erstem Hören (durchgehört oder >30s + Pause); danach dezenter runder Play-Button unten links als Gegenspieler zum FAB, nur in WELT. Audio-Element zentral in index.html → übersteht Welt-Wechsel & Re-Renders. „Gehört" wird hybrid gemerkt: localStorage (sofort/offline) + DB-Flag anthem_heard am User (neue Spalte, über /auth/me, gesetzt via POST /api/profile/anthem-heard) — geräte- und deploy-übergreifend, damit der Banner nicht erneut nervt.
This commit is contained in:
parent
f7370028da
commit
d0a76e1b54
11 changed files with 149 additions and 18 deletions
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '1292'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '1295'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
||||
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
||||
window.APP_VERSION = APP_VERSION;
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ window.Worlds = (() => {
|
|||
track.style.transform = `translateX(${-_cur * (100 / 3)}%)`;
|
||||
_updateDots();
|
||||
_updateFab();
|
||||
_anthem.updateButton();
|
||||
// Karte neu rendern nachdem Transition abgeschlossen
|
||||
if (_cur === 2 && _map) {
|
||||
setTimeout(() => _map.invalidateSize(), animated ? 380 : 50);
|
||||
|
|
@ -1933,6 +1934,83 @@ window.Worlds = (() => {
|
|||
{ t:"Ein Hund braucht keinen Kalender, er weiß genau, wann du nach Hause kommst.", a:"Unbekannt" },
|
||||
];
|
||||
|
||||
// ── HYMNE (Ban Yaro Blues) ───────────────────────────────────
|
||||
// Banner in der WELT-Welt lädt zum ersten Hören ein und verschwindet danach.
|
||||
// Ab dann: dezenter runder Play-Button unten links (Gegenspieler zum FAB).
|
||||
// Audio-Element lebt zentral in index.html → übersteht Re-Renders & Welt-Wechsel.
|
||||
const _anthem = (() => {
|
||||
const KEY = 'by_anthem_heard';
|
||||
let _bound = false;
|
||||
const _audio = () => document.getElementById('anthem-audio');
|
||||
// Gehört? Server-Flag (geräteübergreifend, deploy-fest) ODER lokal (sofort/offline).
|
||||
const heard = () => {
|
||||
if (_state?.user?.anthem_heard) return true;
|
||||
try { return localStorage.getItem(KEY) === '1'; } catch (_) { return false; }
|
||||
};
|
||||
|
||||
function _markHeard() {
|
||||
try { localStorage.setItem(KEY, '1'); } catch (_) {}
|
||||
if (!_state?.user?.anthem_heard) { // server-seitig genau einmal merken
|
||||
if (_state?.user) _state.user.anthem_heard = 1;
|
||||
try { API.post('/profile/anthem-heard', {}).catch(() => {}); } catch (_) {}
|
||||
}
|
||||
document.getElementById('ww-anthem-card')?.classList.add('hidden'); // Banner live ausblenden
|
||||
updateButton();
|
||||
}
|
||||
|
||||
function _sync() {
|
||||
const a = _audio();
|
||||
const playing = !!(a && !a.paused && !a.ended);
|
||||
document.getElementById('ww-anthem-icon')?.querySelector('use')
|
||||
?.setAttribute('href', `/icons/phosphor.svg#${playing ? 'pause' : 'play'}`);
|
||||
const sub = document.getElementById('ww-anthem-sub');
|
||||
if (sub) sub.textContent = playing ? 'läuft … (tippen zum Pausieren)' : 'unsere Hymne · anhören';
|
||||
const btn = document.getElementById('worlds-anthem');
|
||||
if (btn) {
|
||||
btn.classList.toggle('playing', playing);
|
||||
btn.querySelector('use')?.setAttribute('href', `/icons/phosphor.svg#${playing ? 'pause' : 'play'}`);
|
||||
}
|
||||
}
|
||||
|
||||
function _bindAudio() {
|
||||
if (_bound) return;
|
||||
const a = _audio();
|
||||
if (!a) return;
|
||||
_bound = true;
|
||||
a.addEventListener('play', _sync);
|
||||
a.addEventListener('pause', () => { if (a.currentTime >= 30) _markHeard(); _sync(); });
|
||||
a.addEventListener('ended', () => { _markHeard(); _sync(); });
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
const a = _audio();
|
||||
if (!a) return;
|
||||
if (a.paused) a.play().catch(() => {});
|
||||
else a.pause();
|
||||
}
|
||||
|
||||
function updateButton() {
|
||||
document.getElementById('worlds-anthem')?.classList.toggle('hidden', !(_cur === 2 && heard()));
|
||||
}
|
||||
|
||||
// Bei jedem WELT-Render aufrufen: Audio-Listener sichern, Klicks binden, Status setzen.
|
||||
function initWelt() {
|
||||
_bindAudio();
|
||||
const card = document.getElementById('ww-anthem-card');
|
||||
if (card && !card._anthemBound) {
|
||||
card._anthemBound = true;
|
||||
card.addEventListener('click', toggle);
|
||||
card.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); } });
|
||||
}
|
||||
const btn = document.getElementById('worlds-anthem');
|
||||
if (btn && !btn._anthemBound) { btn._anthemBound = true; btn.addEventListener('click', toggle); }
|
||||
_sync();
|
||||
updateButton();
|
||||
}
|
||||
|
||||
return { heard, toggle, updateButton, initWelt };
|
||||
})();
|
||||
|
||||
function _renderWelt() {
|
||||
const el = document.getElementById('ww-content');
|
||||
if (!el) return;
|
||||
|
|
@ -1959,6 +2037,19 @@ window.Worlds = (() => {
|
|||
— ${_esc(quote.a)}
|
||||
</div>
|
||||
</div>
|
||||
${_anthem.heard() ? '' : `
|
||||
<div class="world-info-card" id="ww-anthem-card" role="button" tabindex="0"
|
||||
style="margin-top:10px;display:flex;align-items:center;gap:14px;cursor:pointer">
|
||||
<div style="width:42px;height:42px;border-radius:50%;background:rgba(255,255,255,0.12);
|
||||
display:flex;align-items:center;justify-content:center;flex-shrink:0">
|
||||
<svg class="ph-icon" id="ww-anthem-icon" style="width:20px;height:20px;color:#fff" aria-hidden="true"><use href="/icons/phosphor.svg#play"></use></svg>
|
||||
</div>
|
||||
<div style="flex:1;min-width:0">
|
||||
<div style="font-size:var(--text-sm);font-weight:600;color:#fff">Ban Yaro Blues</div>
|
||||
<div id="ww-anthem-sub" style="font-size:11px;color:rgba(255,255,255,0.45);margin-top:2px">unsere Hymne · anhören</div>
|
||||
</div>
|
||||
<svg class="ph-icon" style="width:15px;height:15px;color:rgba(255,255,255,0.3);flex-shrink:0" aria-hidden="true"><use href="/icons/phosphor.svg#music-notes"></use></svg>
|
||||
</div>`}
|
||||
</div>
|
||||
<div class="world-bottom">
|
||||
<div class="world-chips-grid">
|
||||
|
|
@ -1972,6 +2063,8 @@ window.Worlds = (() => {
|
|||
</div>
|
||||
`;
|
||||
el.querySelectorAll('[data-wnav]').forEach(e => e.addEventListener('click', () => navigateTo(e.dataset.wnav)));
|
||||
|
||||
_anthem.initWelt();
|
||||
}
|
||||
|
||||
// ── HELPERS ──────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue