diff --git a/bin/main/static/css/style.css b/bin/main/static/css/style.css index 3167eda..3a1836b 100644 --- a/bin/main/static/css/style.css +++ b/bin/main/static/css/style.css @@ -577,6 +577,8 @@ body.app { align-items: center; gap: 0.75rem; padding: 0.55rem 1rem; + position: relative; + z-index: 500; } /* Linker Bereich – Banner, gleiche Breite wie Sidebar */ @@ -596,6 +598,7 @@ body.app { display: flex; align-items: center; height: 100%; + padding: 0 0.5rem; } .topbar-banner { height: 3.5rem; @@ -638,20 +641,31 @@ body.app { .topbar-search-overlay { position: absolute; - top: calc(100% + 6px); + top: 100%; left: 0; right: 0; background: var(--color-card); border: 1px solid var(--color-secondary); - border-radius: 10px; + border-top: none; + border-radius: 0 0 10px 10px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); z-index: 600; max-height: 360px; overflow-y: auto; - display: none; + visibility: hidden; + opacity: 0; + transform: translateY(-8px); + pointer-events: none; + transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s; } -.topbar-search-overlay.open { display: block; } +.topbar-search-overlay.open { + visibility: visible; + opacity: 1; + transform: translateY(0); + pointer-events: auto; + transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0s; +} .topbar-search-hint { padding: 0.75rem 1rem; @@ -794,17 +808,29 @@ body.app { position: fixed; background: var(--color-card); border: 1px solid var(--color-secondary); - border-radius: 12px; + border-top: none; + border-radius: 0 0 12px 12px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.65); z-index: 550; width: 360px; max-height: 500px; - display: none; + display: flex; flex-direction: column; overflow: hidden; + visibility: hidden; + opacity: 0; + transform: translateY(-12px); + pointer-events: none; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s; } -.topbar-panel.open { display: flex; } +.topbar-panel.open { + visibility: visible; + opacity: 1; + transform: translateY(0); + pointer-events: auto; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0s; +} .topbar-panel-header { display: flex; @@ -996,7 +1022,12 @@ body.app { .topbar-profile-link:hover { background: var(--color-secondary); } .topbar-profile-link--danger { color: var(--color-primary); } -/* ── Mobile: Topbar ausblenden ── */ +/* ── Mobile: Topbar ausblenden, Content-Rahmen entfernen ── */ @media (max-width: 768px) { .topbar { display: none; } + .main { + border: none; + border-radius: 0; + box-shadow: none; + } } diff --git a/bin/main/static/games/bdsm/aufgaben.html b/bin/main/static/games/bdsm/aufgaben.html new file mode 100644 index 0000000..51a768f --- /dev/null +++ b/bin/main/static/games/bdsm/aufgaben.html @@ -0,0 +1,1776 @@ + + + + + + + Aufgaben – BDSM – xXx Sphere + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+

Meine Aufgabengruppen

+
+ + + + +
+
+
+
Wird geladen…
+
+ +
+ + +
+
+

Abonnierte Aufgabengruppen

+
+ + +
+
+
+
Wird geladen…
+
+ +
+ + +
+
+

System-Aufgabengruppen

+
+ +
+
+
+
Wird geladen…
+
+ +
+ +
+
+ + + + + + diff --git a/bin/main/static/games/bdsm/entdecken.html b/bin/main/static/games/bdsm/entdecken.html new file mode 100644 index 0000000..9ca94d1 --- /dev/null +++ b/bin/main/static/games/bdsm/entdecken.html @@ -0,0 +1,485 @@ + + + + + + + Entdecken – xXx Sphere + + + + + + +
+
+ +
Wird geladen…
+
+ +
+
+ + + + + + diff --git a/bin/main/static/games/common/toys.html b/bin/main/static/games/bdsm/toys.html similarity index 100% rename from bin/main/static/games/common/toys.html rename to bin/main/static/games/bdsm/toys.html diff --git a/bin/main/static/games/common/einladungen.html b/bin/main/static/games/common/einladungen.html deleted file mode 100644 index c55905d..0000000 --- a/bin/main/static/games/common/einladungen.html +++ /dev/null @@ -1,982 +0,0 @@ - - - - - - - Einladungen – xXx Sphere - - - - - -
-
-

Einladungen

- -
- - -
- - -
-
- - -
- - -
-
- - -
-
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- -
-
🎲
-
-
-
Vanilla Game – Einladung
-
-
-

- Du wurdest zu einem Vanilla Game eingeladen. Wie möchtest du mitspielen? -

-
-
- - - -
-
-
- - -
-
-
- -
-
⛓️
-
-
-
BDSM Game – Einladung
-
-
-

- Du wurdest zu einem BDSM Game eingeladen. Wie möchtest du mitspielen? -

-
-
- - - -
-
-
- - -
-
-
- -
-
🔒
-
-
-
-
-
-
-
-
-
- - - Ziffern -
-
-
-
- - -
-
-
- - -
-
-
-
🔒
-

Dein Entsperrcode

-

- Stelle die Kombination deines Tresors auf den folgenden Code ein und verschließe deinen Schlüssel in diesem. -

-
- - -
-
- - - - - - - - diff --git a/bin/main/static/games/common/aufgaben.html b/bin/main/static/games/vanilla/aufgaben.html similarity index 96% rename from bin/main/static/games/common/aufgaben.html rename to bin/main/static/games/vanilla/aufgaben.html index 65d97b6..ec355e0 100644 --- a/bin/main/static/games/common/aufgaben.html +++ b/bin/main/static/games/vanilla/aufgaben.html @@ -4,7 +4,7 @@ - Aufgaben – xXx Sphere + Aufgaben – Vanilla – xXx Sphere + + + +
+
+ +
Wird geladen…
+
+ +
+
+ + + + + + diff --git a/bin/main/static/games/vanilla/neuvanilla.html b/bin/main/static/games/vanilla/neuvanilla.html index 027f413..559bed4 100644 --- a/bin/main/static/games/vanilla/neuvanilla.html +++ b/bin/main/static/games/vanilla/neuvanilla.html @@ -191,7 +191,7 @@

- Gruppen verwalten: Aufgaben-Verwaltung (Vanilla) + Gruppen verwalten: Aufgaben-Verwaltung (Vanilla)

diff --git a/src/main/resources/static/games/common/toys.html b/bin/main/static/games/vanilla/toys.html similarity index 100% rename from src/main/resources/static/games/common/toys.html rename to bin/main/static/games/vanilla/toys.html diff --git a/bin/main/static/img/card_old.png b/bin/main/static/img/card_old.png deleted file mode 100644 index b3e1068..0000000 Binary files a/bin/main/static/img/card_old.png and /dev/null differ diff --git a/bin/main/static/js/mobile-nav.js b/bin/main/static/js/mobile-nav.js new file mode 100644 index 0000000..6fab66a --- /dev/null +++ b/bin/main/static/js/mobile-nav.js @@ -0,0 +1,459 @@ +(function () { + if (window.__mobileNavLoaded) return; + window.__mobileNavLoaded = true; + + const path = window.location.pathname; + const I = window.IC || function () { return ''; }; + const TOPBAR_H = '4.875rem'; + + // ── CSS ────────────────────────────────────────────────────────────────── + const style = document.createElement('style'); + style.textContent = ` + .mobile-topbar { + display: none; + position: fixed; + top: 0; left: 0; right: 0; + height: ${TOPBAR_H}; + background: var(--color-card); + border-bottom: 1px solid var(--color-secondary); + box-shadow: 0 2px 12px rgba(0,0,0,0.4); + z-index: 500; + align-items: center; + padding: 0 0.25rem; + } + .mobile-topbar-logo { + position: absolute; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + pointer-events: auto; + } + .mobile-topbar-logo img { + height: 3rem; + width: auto; + } + .mobile-topbar-actions { + display: flex; + align-items: center; + margin-left: auto; + } + .mobile-tb-btn { + position: relative; + background: none; + border: none; + color: var(--color-text); + font-size: 1.725rem; + line-height: 1; + padding: 0.75rem 0.675rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + border-radius: 12px; + text-decoration: none; + transition: background 0.12s; + flex-shrink: 0; + } + .mobile-tb-btn:hover { background: var(--color-secondary); } + .mobile-tb-badge { + position: absolute; + top: 3px; right: 3px; + background: var(--color-primary); + color: #fff; + border-radius: 10px; + font-size: 0.87rem; + font-weight: 700; + min-width: 1.65em; + padding: 0.15em 0.375em; + display: none; + line-height: 1.4; + text-align: center; + } + .mobile-tb-avatar { + width: 2.7rem; + height: 2.7rem; + border-radius: 50%; + object-fit: cover; + } + + /* ── Mobile Menu Backdrop ── */ + .mob-menu-backdrop { + display: none; + position: fixed; + top: ${TOPBAR_H}; + left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.55); + z-index: 998; + } + .mob-menu-backdrop.open { display: block; } + + /* ── Mobile Menu Panel ── */ + .mob-menu-panel { + position: fixed; + top: ${TOPBAR_H}; + right: 0; + bottom: 0; + width: min(80%, 360px); + background: var(--color-card); + border-left: 1px solid var(--color-secondary); + z-index: 999; + overflow-y: auto; + transform: translateX(100%); + transition: transform 0.25s ease; + } + .mob-menu-panel.open { transform: translateX(0); } + + /* ── Accordion ── */ + .mnav-section { border-bottom: 1px solid var(--color-secondary); } + .mnav-section:last-child { border-bottom: none; } + + .mnav-section-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.85rem 1.1rem; + cursor: pointer; + user-select: none; + font-size: 0.88rem; + font-weight: 600; + color: var(--color-text); + transition: background 0.12s; + } + .mnav-section-header:hover { background: var(--color-secondary); } + + .mnav-section-arrow { + font-size: 0.65rem; + transition: transform 0.2s; + color: var(--color-muted); + } + .mnav-section.open .mnav-section-arrow { transform: rotate(90deg); } + + .mnav-section-body { display: none; } + .mnav-section.open .mnav-section-body { display: block; } + + /* ── Menu Links ── */ + .mnav-link { + display: flex; + align-items: center; + gap: 0.65rem; + padding: 0.6rem 1.1rem 0.6rem 1.5rem; + text-decoration: none; + color: var(--color-text); + font-size: 0.9rem; + transition: background 0.12s, color 0.12s; + } + .mnav-link:hover { background: var(--color-secondary); color: var(--color-primary); } + .mnav-link.active { color: var(--color-primary); background: rgba(var(--color-primary-rgb,233,69,96),0.08); } + .mnav-icon { width: 1.3rem; text-align: center; font-size: 1rem; flex-shrink: 0; } + .mnav-badge { + margin-left: auto; + background: var(--color-primary); color: #fff; + border-radius: 10px; font-size: 0.68rem; font-weight: 700; + min-width: 1.2em; padding: 0.1em 0.3em; display: none; + } + .mnav-link--danger { color: var(--color-primary); } + .mnav-link--danger:hover { background: rgba(var(--color-primary-rgb,233,69,96),0.1); color: var(--color-primary); } + + /* ── Show only on mobile ── */ + @media (max-width: 768px) { + .mobile-topbar { display: flex; } + body.app { padding-top: ${TOPBAR_H}; } + } + `; + document.head.appendChild(style); + + // ── Helper ─────────────────────────────────────────────────────────────── + function lnk(href, iconKey, label, id, badgeId) { + const cls = path === href ? ' active' : ''; + const idAttr = id ? ` id="${id}"` : ''; + const badge = badgeId ? `` : ''; + const icon = iconKey ? `${I(iconKey) || ''}` : ``; + return `${icon}${label}${badge}`; + } + + // ── Menu-Sektionen ──────────────────────────────────────────────────────── + const SECTIONS = [ + { + label: 'Allgemein', + prefixes: ['/userhome.html', '/search.html', '/community/nachrichten.html', + '/community/benachrichtigungen.html', '/games/common/einladungen.html'], + html: ` + ${lnk('/userhome.html', 'HOME', 'Home' )} + ${lnk('/search.html', 'SEARCH', 'Suche' )} + + ${lnk('/community/nachrichten.html', 'MESSAGES', 'Nachrichten', null, 'mnavBadgeMsg' )} + ${lnk('/community/benachrichtigungen.html', 'NOTIFICATIONS', 'Benachrichtigungen', null, 'mnavBadgeNotif' )} + ${lnk('/games/common/einladungen.html', 'INVITATIONS', 'Einladungen', null, 'mnavBadgeInv' )} + `, + }, + { + label: 'Community', + prefixes: ['/community/'], + html: ` + ${lnk('/community/feed.html', 'FEED', 'Feed' )} + ${lnk('/community/freunde.html', 'FRIENDS', 'Freunde', null, 'mnavBadgeFriends' )} + ${lnk('/community/gruppen.html', 'GROUPS', 'Gruppen', null, 'mnavBadgeGruppen' )} + ${lnk('/community/locations.html', 'LOCATION', 'Locations' )} + ${lnk('/community/events.html', 'EVENT', 'Veranstaltungen' )} + `, + }, + { + label: 'Dating', + prefixes: ['/dating/'], + html: ` + + ${I('DATING') || '♥'}Dating + ${lnk('/dating/besucher.html', '', 'Besucher')} + ${lnk('/dating/likes.html', '', 'Likes' )} + ${lnk('/dating/matches.html', '', 'Matches' )} + `, + }, + { + label: 'Vanilla Game', + prefixes: ['/games/vanilla/'], + html: ` + ${lnk('/games/vanilla/neuvanilla.html', 'PLAY_NEW', 'Neue Session', 'mnavVanillaNeu' )} + + + ${lnk('/games/vanilla/aufgaben.html', 'CHECK', 'Aufgaben' )} + ${lnk('/games/vanilla/toys.html', 'TOYS', 'Toys' )} + ${lnk('/games/vanilla/entdecken.html', 'DISCOVER', 'Entdecken' )} + `, + }, + { + label: 'BDSM Game', + prefixes: ['/games/bdsm/'], + html: ` + ${lnk('/games/bdsm/neubdsm.html', 'PLAY_NEW', 'Neue Session', 'mnavBdsmNeu' )} + + + ${lnk('/games/bdsm/aufgaben.html', 'CHECK', 'Aufgaben' )} + ${lnk('/games/bdsm/toys.html', 'TOYS', 'Toys' )} + ${lnk('/games/bdsm/entdecken.html', 'DISCOVER', 'Entdecken' )} + `, + }, + { + label: 'Chastity Game', + prefixes: ['/games/chastity/'], + html: ` + ${lnk('/games/chastity/neulock.html', 'NEW_LOCK', 'Neues Lock', 'mnavChastityNeu' )} + + ${lnk('/games/chastity/communityvotes.html', 'VOTES', 'Community Votes' )} + ${lnk('/games/chastity/meine-locks.html', 'LOCK', 'Meine Vorlagen' )} + ${lnk('/games/chastity/entdecken-vorlagen.html', 'DISCOVER', 'Entdecken' )} + ${lnk('/games/chastity/keyholder-finden.html', 'FRIENDS', 'Keyholder finden' )} + ${lnk('/games/chastity/keyholder.html', 'KEY', 'Keyholder' )} + ${lnk('/games/chastity/unlock-history.html', 'HISTORY', 'Code-Historie' )} + `, + }, + { + label: 'Konto', + prefixes: ['/konto/', '/help/'], + html: ` + ${lnk('/konto/einstellungen.html', 'SETTINGS', 'Einstellungen')} + ${lnk('/help/overview.html', 'HELP', 'Hilfe' )} + + ${I('LOGOUT') || ''}Abmelden + `, + }, + ]; + + // ── Mobile Topbar ───────────────────────────────────────────────────────── + const topbarEl = document.createElement('div'); + topbarEl.className = 'mobile-topbar'; + topbarEl.id = 'mobileTopbar'; + topbarEl.innerHTML = ` + + + `; + document.body.insertAdjacentElement('afterbegin', topbarEl); + + // ── Overlay ─────────────────────────────────────────────────────────────── + const backdropEl = document.createElement('div'); + backdropEl.className = 'mob-menu-backdrop'; + backdropEl.id = 'mobMenuBackdrop'; + + const panelEl = document.createElement('div'); + panelEl.className = 'mob-menu-panel'; + panelEl.id = 'mobMenuPanel'; + panelEl.innerHTML = SECTIONS.map(s => { + const isOpen = s.prefixes.some(p => path.startsWith(p) || path === p); + return ` + `; + }).join(''); + + document.body.appendChild(backdropEl); + document.body.appendChild(panelEl); + + // ── Accordion (nur eine Sektion gleichzeitig offen) ────────────────────── + panelEl.querySelectorAll('.mnav-section-header').forEach(h => { + h.addEventListener('click', () => { + const section = h.closest('.mnav-section'); + const isOpen = section.classList.contains('open'); + panelEl.querySelectorAll('.mnav-section').forEach(s => s.classList.remove('open')); + if (!isOpen) section.classList.add('open'); + }); + }); + + // ── Open / Close ────────────────────────────────────────────────────────── + function openMenu() { + panelEl.classList.add('open'); + backdropEl.classList.add('open'); + } + function closeMenu() { + panelEl.classList.remove('open'); + backdropEl.classList.remove('open'); + } + + document.getElementById('mobMenuToggle').addEventListener('click', e => { + e.stopPropagation(); + panelEl.classList.contains('open') ? closeMenu() : openMenu(); + }); + backdropEl.addEventListener('click', closeMenu); + document.addEventListener('keydown', e => { if (e.key === 'Escape') closeMenu(); }); + panelEl.querySelectorAll('.mnav-link').forEach(l => { + l.addEventListener('click', () => { if (l.getAttribute('href') !== '#') closeMenu(); }); + }); + + // ── Badges ──────────────────────────────────────────────────────────────── + function setBadge(id, n) { + const el = document.getElementById(id); + if (!el) return; + el.textContent = n > 99 ? '99+' : n; + el.style.display = n > 0 ? 'inline-block' : 'none'; + } + + fetch('/social/messages/unread/count').then(r => r.ok ? r.json() : 0).then(n => { + setBadge('mobTbMsgBadge', n); setBadge('mnavBadgeMsg', n); + }).catch(() => {}); + + fetch('/notifications/unread/count').then(r => r.ok ? r.json() : 0).then(n => { + setBadge('mobTbNotifBadge', n); setBadge('mnavBadgeNotif', n); + }).catch(() => {}); + + Promise.all([ + fetch('/lockee/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/keyholder/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/bdsm/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/vanilla/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + ]).then(([l, k, b, v]) => { + const n = l + k + b + v; + setBadge('mobTbInvBadge', n); setBadge('mnavBadgeInv', n); + }).catch(() => {}); + + fetch('/social/friends/pending/count').then(r => r.ok ? r.json() : 0) + .then(n => setBadge('mnavBadgeFriends', n)).catch(() => {}); + + Promise.all([ + fetch('/gruppen/requests/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/gruppen/reports/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + ]).then(([j, rep]) => setBadge('mnavBadgeGruppen', j + rep)).catch(() => {}); + + // ── User / Dynamische Links ─────────────────────────────────────────────── + fetch('/login/me').then(r => r.ok ? r.json() : null).then(async user => { + if (!user) return; + + // Profilbild + const profileBtn = document.getElementById('mobTbProfileBtn'); + if (profileBtn) { + profileBtn.href = '/community/benutzer.html?userId=' + user.userId; + if (user.profilePicture) { + profileBtn.innerHTML = + ``; + } + } + + // Admin + if (user.admin) { + const el = document.getElementById('mnavAdminLink'); + if (el) el.style.display = ''; + } + + // Dating + const datingLink = document.getElementById('mnavDatingLink'); + if (datingLink) { + datingLink.href = user.datingAktiv + ? '/dating/dating.html' + : '/konto/einstellungen.html#sec-dating'; + } + + const hide = id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; }; + const show = id => { const el = document.getElementById(id); if (el) el.style.display = ''; }; + const setHref = (id, h) => { const el = document.getElementById(id); if (el) el.href = h; }; + + // BDSM + try { + const r = await fetch('/bdsm/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('mnavBdsmNeu'); hide('mnavBdsmImSpiel'); show('mnavBdsmAktiv'); + setHref('mnavBdsmAktiv', aktiv.sessionId ? '/games/bdsm/bdsmingame.html' : '/games/bdsm/neubdsm.html'); + } else { + const sr = await fetch(`/bdsm?userId=${user.userId}`); + if (sr.status === 200) { hide('mnavBdsmNeu'); show('mnavBdsmImSpiel'); } + else show('mnavBdsmNeu'); + } + } catch (_) {} + + // Vanilla + try { + const r = await fetch('/vanilla/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('mnavVanillaNeu'); hide('mnavVanillaImSpiel'); show('mnavVanillaAktiv'); + setHref('mnavVanillaAktiv', aktiv.sessionId ? '/games/vanilla/vanillaingame.html' : '/games/vanilla/neuvanilla.html'); + } else { + const sr = await fetch(`/vanilla?userId=${user.userId}`); + if (sr.status === 200) { hide('mnavVanillaNeu'); show('mnavVanillaImSpiel'); } + else show('mnavVanillaNeu'); + } + } catch (_) {} + + // Chastity + try { + const r = await fetch('/keyholder/mylock'); + if (r.ok) { + const lock = await r.json(); + show('mnavChastityAktiv'); + setHref('mnavChastityAktiv', '/games/chastity/activelock.html?lockId=' + lock.lockId); + } + } catch (_) {} + }).catch(() => {}); +})(); diff --git a/bin/main/static/js/nav.js b/bin/main/static/js/nav.js index 4073704..70fa159 100644 --- a/bin/main/static/js/nav.js +++ b/bin/main/static/js/nav.js @@ -8,23 +8,48 @@ /* ── Burger-Button ── */ .nav-burger { display: inline-flex; align-items: center; gap: 0.45rem; - padding: 0.35rem 0.8rem 0.35rem 0.6rem; + padding: 0.46rem 0.8rem 0.46rem 0.6rem; background: none; border: 1px solid var(--color-secondary); border-radius: 8px; cursor: pointer; color: var(--color-text); font-size: 0.88rem; font-weight: 600; flex-shrink: 0; transition: border-color 0.15s, color 0.15s; margin-right: 0.5rem; + line-height: 1; } .nav-burger:hover { border-color: var(--color-primary); color: var(--color-primary); } - .nav-burger-icon { font-size: 1.05rem; line-height: 1; } + .nav-burger-icon { + font-size: 1.05rem; line-height: 1; + position: relative; + display: inline-flex; align-items: center; justify-content: center; + width: 1.2em; height: 1.2em; + } + .nav-burger-icon-menu, + .nav-burger-icon-close { + position: absolute; + inset: 0; + display: flex; align-items: center; justify-content: center; + transition: opacity 0.15s, transform 0.15s; + } + .nav-burger-icon-close { opacity: 0; transform: rotate(-45deg); } + #topbar.nav-menu-open .nav-burger .nav-burger-icon-menu { opacity: 0; transform: rotate(45deg); } + #topbar.nav-menu-open .nav-burger .nav-burger-icon-close { opacity: 1; transform: rotate(0deg); } /* ── Backdrop ── */ .nav-backdrop { - display: none; position: fixed; inset: 0; z-index: 498; + position: fixed; inset: 0; z-index: 498; + background: rgba(0,0,0,0); + pointer-events: none; + transition: background 0.2s ease; + } + .nav-backdrop.open { + background: rgba(0,0,0,0.55); + pointer-events: auto; } - .nav-backdrop.open { display: block; } /* ── Topbar: untere Rundung entfernen wenn Menü offen ── */ + #topbar { + transition: border-radius 0.22s ease, border-bottom-color 0.22s ease; + } #topbar.nav-menu-open { border-radius: 12px 12px 0 0; border-bottom-color: transparent; @@ -38,11 +63,21 @@ border-top: none; border-radius: 0 0 12px 12px; box-shadow: 0 12px 40px rgba(0,0,0,0.55); - display: none; overflow-y: auto; max-height: calc(100vh - 80px); + visibility: hidden; + opacity: 0; + transform: translateY(-12px); + pointer-events: none; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s; + } + .nav-dropdown.open { + visibility: visible; + opacity: 1; + transform: translateY(0); + pointer-events: auto; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0s; } - .nav-dropdown.open { display: block; } /* ── 4-Spalten-Layout ── */ .nav-columns { @@ -226,17 +261,17 @@ { href: '/games/vanilla/neuvanilla.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navVanillaNeu' }, { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navVanillaAktiv' }, { href: '/games/vanilla/vanillaingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navVanillaImSpiel' }, - { href: '/games/common/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: 'TOYS', label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + { href: '/games/vanilla/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/vanilla/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/vanilla/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, ])} ${gameGroup('BDSM', 'BDSM Game', [ - { href: '/games/bdsm/neubdsm.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navBdsmNeu' }, - { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navBdsmAktiv' }, - { href: '/games/bdsm/bdsmingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navBdsmImSpiel' }, - { href: '/games/common/aufgaben.html?mode=bdsm', icon: 'CHECK', label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: 'TOYS', label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + { href: '/games/bdsm/neubdsm.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navBdsmNeu' }, + { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navBdsmAktiv' }, + { href: '/games/bdsm/bdsmingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navBdsmImSpiel' }, + { href: '/games/bdsm/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/bdsm/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/bdsm/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, ])} ${gameGroup('CHASTITY', 'Chastity Game', [ { href: '/games/chastity/neulock.html', icon: 'NEW_LOCK', label: 'Neues Lock', id: 'navChastityNeu' }, @@ -330,8 +365,8 @@ btn.className = 'nav-burger'; btn.id = 'navBurgerBtn'; btn.setAttribute('aria-label', 'Menü öffnen'); - btn.innerHTML = `${I('MENU') || '☰'}Menü`; - topbarLeft.prepend(btn); + btn.innerHTML = `${I('MENU') || '≡'}${I('CLOSE') || 'x'}Menü`; + topbarLeft.append(btn); btn.addEventListener('click', e => { e.stopPropagation(); const dd = document.getElementById('navDropdown'); @@ -460,4 +495,6 @@ } loadScript('/js/topbar.js'); loadScript('/js/social-sidebar.js'); + loadScript('/js/section-nav.js'); + loadScript('/js/mobile-nav.js'); })(); diff --git a/bin/main/static/js/section-nav.js b/bin/main/static/js/section-nav.js new file mode 100644 index 0000000..4328936 --- /dev/null +++ b/bin/main/static/js/section-nav.js @@ -0,0 +1,243 @@ +(function () { + const path = window.location.pathname; + const search = window.location.search; + const I = window.IC || function () { return ''; }; + + // ── Bereichs-Definitionen ──────────────────────────────────────────────── + const SECTIONS = { + social: { + prefixes: ['/community/'], + exclude: [ + '/community/nachrichten.html', + '/community/benachrichtigungen.html', + '/community/einladungen.html', + ], + items: [ + { href: '/community/feed.html', icon: 'FEED', label: 'Feed' }, + { href: '/community/freunde.html', icon: 'FRIENDS', label: 'Freunde' }, + { href: '/community/gruppen.html', icon: 'GROUPS', label: 'Gruppen' }, + { href: '/community/locations.html', icon: 'LOCATION', label: 'Locations' }, + { href: '/community/events.html', icon: 'EVENT', label: 'Veranstaltungen' }, + ], + }, + dating: { + prefixes: ['/dating/'], + items: [ + { href: '/dating/dating.html', icon: 'DATING', label: 'Dating', id: 'snavDatingLink' }, + { href: '/dating/besucher.html', icon: '', label: 'Besucher' }, + { href: '/dating/likes.html', icon: '', label: 'Likes' }, + { href: '/dating/matches.html', icon: '', label: 'Matches' }, + ], + }, + vanilla: { + prefixes: ['/games/vanilla/'], + items: [ + { href: '/games/vanilla/neuvanilla.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'snavVanillaNeu' }, + { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'snavVanillaAktiv', hidden: true }, + { href: '/games/vanilla/vanillaingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'snavVanillaImSpiel', hidden: true }, + { href: '/games/vanilla/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/vanilla/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/vanilla/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + ], + }, + bdsm: { + prefixes: ['/games/bdsm/'], + items: [ + { href: '/games/bdsm/neubdsm.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'snavBdsmNeu' }, + { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'snavBdsmAktiv', hidden: true }, + { href: '/games/bdsm/bdsmingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'snavBdsmImSpiel', hidden: true }, + { href: '/games/bdsm/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/bdsm/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/bdsm/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + ], + }, + chastity: { + prefixes: ['/games/chastity/'], + items: [ + { href: '/games/chastity/neulock.html', icon: 'NEW_LOCK', label: 'Neues Lock', id: 'snavChastityNeu' }, + { href: '#', icon: 'ACTIVE_LOCK', label: 'Aktives Lock', id: 'snavChastityAktiv', hidden: true }, + { href: '/games/chastity/communityvotes.html', icon: 'VOTES', label: 'Community Votes' }, + { href: '/games/chastity/meine-locks.html', icon: 'LOCK', label: 'Meine Vorlagen' }, + { href: '/games/chastity/entdecken-vorlagen.html', icon: 'DISCOVER', label: 'Entdecken' }, + { href: '/games/chastity/keyholder-finden.html', icon: 'FRIENDS', label: 'Keyholder finden' }, + { href: '/games/chastity/keyholder.html', icon: 'KEY', label: 'Keyholder' }, + { href: '/games/chastity/unlock-history.html', icon: 'HISTORY', label: 'Code-Historie' }, + ], + }, + }; + + // ── Aktiven Bereich ermitteln ──────────────────────────────────────────── + const sectionKey = Object.keys(SECTIONS).find(k => { + const s = SECTIONS[k]; + if (!s.prefixes.some(p => path.startsWith(p))) return false; + if (s.exclude && s.exclude.includes(path)) return false; + return true; + }); + if (!sectionKey) return; + const section = SECTIONS[sectionKey]; + + // ── CSS ────────────────────────────────────────────────────────────────── + const style = document.createElement('style'); + style.textContent = ` + .section-nav { + display: flex; + align-items: center; + gap: 0.15rem; + flex-wrap: wrap; + padding: 0 0 0.6rem 0; + } + .section-nav-link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.3rem 0.75rem; + border-radius: 7px; + text-decoration: none; + color: var(--color-muted); + font-size: 0.85rem; + font-weight: 500; + white-space: nowrap; + transition: background 0.12s, color 0.12s; + } + .section-nav-link:hover { + background: var(--color-secondary); + color: var(--color-text); + } + .section-nav-link.active { + color: var(--color-primary); + background: rgba(var(--color-primary-rgb, 233,69,96), 0.09); + font-weight: 600; + } + .section-nav-icon { + font-size: 0.9rem; + line-height: 1; + flex-shrink: 0; + } + .section-nav--icons-only .section-nav-label { display: none; } + .section-nav--icons-only .section-nav-link { gap: 0; padding: 0.3rem 0.55rem; } + .section-nav-sep { + border: none; + border-top: 1px solid var(--color-secondary); + margin: 0 0 1.25rem 0; + } + `; + document.head.appendChild(style); + + // ── Aktiv-Erkennung ────────────────────────────────────────────────────── + function isActive(item) { + if (item.href === '#') return false; + const [itemPath, itemQuery] = item.href.split('?'); + if (itemQuery) return path === itemPath && search === '?' + itemQuery; + return path === itemPath; + } + + // ── Nav bauen ──────────────────────────────────────────────────────────── + const navEl = document.createElement('nav'); + navEl.className = 'section-nav'; + + section.items.forEach(item => { + const a = document.createElement('a'); + a.href = item.href; + a.className = 'section-nav-link' + (isActive(item) ? ' active' : ''); + if (item.id) a.id = item.id; + if (item.hidden) a.style.display = 'none'; + a.title = item.label; + if (item.icon) a.innerHTML += `${I(item.icon) || ''}`; + a.innerHTML += `${item.label}`; + navEl.appendChild(a); + }); + + const sep = document.createElement('hr'); + sep.className = 'section-nav-sep'; + + // ── Einfügen in .main ──────────────────────────────────────────────────── + function checkOverflow() { + // Immer zuerst Labels einblenden und ohne wrap messen — + // so gibt es keine Feedback-Schleife durch Größenänderung nach dem Umschalten + navEl.classList.remove('section-nav--icons-only'); + navEl.style.flexWrap = 'nowrap'; + const overflows = navEl.scrollWidth > navEl.clientWidth; + navEl.style.flexWrap = ''; + if (overflows) navEl.classList.add('section-nav--icons-only'); + } + + function inject() { + const main = document.querySelector('.main'); + if (!main) { setTimeout(inject, 30); return; } + main.insertBefore(sep, main.firstChild); + main.insertBefore(navEl, sep); + loadDynamic(); + // Overflow-Erkennung beim Laden und bei Größenänderung + requestAnimationFrame(checkOverflow); + new ResizeObserver(checkOverflow).observe(navEl); + } + inject(); + + // ── Dynamische Elemente (analog nav.js) ────────────────────────────────── + function hide(id) { const el = document.getElementById(id); if (el) el.style.display = 'none'; } + function show(id) { const el = document.getElementById(id); if (el) el.style.display = ''; } + function setHref(id, h) { const el = document.getElementById(id); if (el) el.href = h; } + + async function loadDynamic() { + try { + const res = await fetch('/login/me'); + if (!res.ok) return; + const user = await res.json(); + if (!user) return; + + // Dating-Link + const datingLink = document.getElementById('snavDatingLink'); + if (datingLink) { + datingLink.href = user.datingAktiv + ? '/dating/dating.html' + : '/konto/einstellungen.html#sec-dating'; + } + + // BDSM + if (sectionKey === 'bdsm') { + try { + const r = await fetch('/bdsm/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('snavBdsmNeu'); hide('snavBdsmImSpiel'); + show('snavBdsmAktiv'); + setHref('snavBdsmAktiv', aktiv.sessionId ? '/games/bdsm/bdsmingame.html' : '/games/bdsm/neubdsm.html'); + } else { + const sr = await fetch(`/bdsm?userId=${user.userId}`); + if (sr.status === 200) { hide('snavBdsmNeu'); show('snavBdsmImSpiel'); } + else show('snavBdsmNeu'); + } + } catch (_) { show('snavBdsmNeu'); } + } + + // Vanilla + if (sectionKey === 'vanilla') { + try { + const r = await fetch('/vanilla/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('snavVanillaNeu'); hide('snavVanillaImSpiel'); + show('snavVanillaAktiv'); + setHref('snavVanillaAktiv', aktiv.sessionId ? '/games/vanilla/vanillaingame.html' : '/games/vanilla/neuvanilla.html'); + } else { + const sr = await fetch(`/vanilla?userId=${user.userId}`); + if (sr.status === 200) { hide('snavVanillaNeu'); show('snavVanillaImSpiel'); } + else show('snavVanillaNeu'); + } + } catch (_) { show('snavVanillaNeu'); } + } + + // Chastity + if (sectionKey === 'chastity') { + try { + const r = await fetch('/keyholder/mylock'); + if (r.ok) { + const lock = await r.json(); + show('snavChastityAktiv'); + setHref('snavChastityAktiv', '/games/chastity/activelock.html?lockId=' + lock.lockId); + } + } catch (_) {} + } + } catch (_) {} + } +})(); diff --git a/bin/main/static/js/sidebar.js b/bin/main/static/js/sidebar.js index 2ddd7bf..80ea841 100644 --- a/bin/main/static/js/sidebar.js +++ b/bin/main/static/js/sidebar.js @@ -10,9 +10,9 @@ { href: '/games/vanilla/neuvanilla.html', icon: I('PLAY_NEW'), label: 'Neue Session', id: 'navVanillaNeu' }, { href: '#', icon: I('WAITING'), label: 'Aktive Session', id: 'navVanillaAktiv' }, { href: '/games/vanilla/vanillaingame.html', icon: I('PLAY_ACTIVE'), label: 'Im Spiel', id: 'navVanillaImSpiel' }, - { href: '/games/common/aufgaben.html', icon: I('CHECK'), label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: I('TOYS'), label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, + { href: '/games/vanilla/aufgaben.html', icon: I('CHECK'), label: 'Aufgaben' }, + { href: '/games/vanilla/toys.html', icon: I('TOYS'), label: 'Toys' }, + { href: '/games/vanilla/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, ] }, { @@ -22,9 +22,9 @@ { href: '/games/bdsm/neubdsm.html', icon: I('PLAY_NEW'), label: 'Neue Session', id: 'navBdsmNeu' }, { href: '#', icon: I('WAITING'), label: 'Aktive Session', id: 'navBdsmAktiv' }, { href: '/games/bdsm/bdsmingame.html', icon: I('PLAY_ACTIVE'), label: 'Im Spiel', id: 'navBdsmImSpiel' }, - { href: '/games/common/aufgaben.html?mode=bdsm', icon: I('CHECK'), label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: I('TOYS'), label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, + { href: '/games/bdsm/aufgaben.html', icon: I('CHECK'), label: 'Aufgaben' }, + { href: '/games/bdsm/toys.html', icon: I('TOYS'), label: 'Toys' }, + { href: '/games/bdsm/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, ] }, { diff --git a/bin/main/static/search.html b/bin/main/static/search.html index 3f0b2d5..5d193a1 100644 --- a/bin/main/static/search.html +++ b/bin/main/static/search.html @@ -165,7 +165,7 @@ .search-load-more:hover { border-color: var(--color-primary); color: var(--color-primary); background: none; } - +

Suche

diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index 3167eda..3a1836b 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -577,6 +577,8 @@ body.app { align-items: center; gap: 0.75rem; padding: 0.55rem 1rem; + position: relative; + z-index: 500; } /* Linker Bereich – Banner, gleiche Breite wie Sidebar */ @@ -596,6 +598,7 @@ body.app { display: flex; align-items: center; height: 100%; + padding: 0 0.5rem; } .topbar-banner { height: 3.5rem; @@ -638,20 +641,31 @@ body.app { .topbar-search-overlay { position: absolute; - top: calc(100% + 6px); + top: 100%; left: 0; right: 0; background: var(--color-card); border: 1px solid var(--color-secondary); - border-radius: 10px; + border-top: none; + border-radius: 0 0 10px 10px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); z-index: 600; max-height: 360px; overflow-y: auto; - display: none; + visibility: hidden; + opacity: 0; + transform: translateY(-8px); + pointer-events: none; + transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s; } -.topbar-search-overlay.open { display: block; } +.topbar-search-overlay.open { + visibility: visible; + opacity: 1; + transform: translateY(0); + pointer-events: auto; + transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0s; +} .topbar-search-hint { padding: 0.75rem 1rem; @@ -794,17 +808,29 @@ body.app { position: fixed; background: var(--color-card); border: 1px solid var(--color-secondary); - border-radius: 12px; + border-top: none; + border-radius: 0 0 12px 12px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.65); z-index: 550; width: 360px; max-height: 500px; - display: none; + display: flex; flex-direction: column; overflow: hidden; + visibility: hidden; + opacity: 0; + transform: translateY(-12px); + pointer-events: none; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s; } -.topbar-panel.open { display: flex; } +.topbar-panel.open { + visibility: visible; + opacity: 1; + transform: translateY(0); + pointer-events: auto; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0s; +} .topbar-panel-header { display: flex; @@ -996,7 +1022,12 @@ body.app { .topbar-profile-link:hover { background: var(--color-secondary); } .topbar-profile-link--danger { color: var(--color-primary); } -/* ── Mobile: Topbar ausblenden ── */ +/* ── Mobile: Topbar ausblenden, Content-Rahmen entfernen ── */ @media (max-width: 768px) { .topbar { display: none; } + .main { + border: none; + border-radius: 0; + box-shadow: none; + } } diff --git a/src/main/resources/static/games/bdsm/aufgaben.html b/src/main/resources/static/games/bdsm/aufgaben.html new file mode 100644 index 0000000..51a768f --- /dev/null +++ b/src/main/resources/static/games/bdsm/aufgaben.html @@ -0,0 +1,1776 @@ + + + + + + + Aufgaben – BDSM – xXx Sphere + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+

Meine Aufgabengruppen

+
+ + + + +
+
+
+
Wird geladen…
+
+ +
+ + +
+
+

Abonnierte Aufgabengruppen

+
+ + +
+
+
+
Wird geladen…
+
+ +
+ + +
+
+

System-Aufgabengruppen

+
+ +
+
+
+
Wird geladen…
+
+ +
+ +
+
+ + + + + + diff --git a/src/main/resources/static/games/bdsm/entdecken.html b/src/main/resources/static/games/bdsm/entdecken.html new file mode 100644 index 0000000..9ca94d1 --- /dev/null +++ b/src/main/resources/static/games/bdsm/entdecken.html @@ -0,0 +1,485 @@ + + + + + + + Entdecken – xXx Sphere + + + + + + +
+
+ +
Wird geladen…
+
+ +
+
+ + + + + + diff --git a/src/main/resources/static/games/bdsm/toys.html b/src/main/resources/static/games/bdsm/toys.html new file mode 100644 index 0000000..a2b3e67 --- /dev/null +++ b/src/main/resources/static/games/bdsm/toys.html @@ -0,0 +1,642 @@ + + + + + + + Toys – xXx Sphere + + + + + + + + + +
+
+ + +
+
+

Meine Toys

+
+ + + +
+
+
+
+ +
+
+ + +
+
+

System-Toys

+
+ +
+
+
+
+ +
+
+ +
+
+ + + + + + diff --git a/src/main/resources/static/games/common/einladungen.html b/src/main/resources/static/games/common/einladungen.html deleted file mode 100644 index c55905d..0000000 --- a/src/main/resources/static/games/common/einladungen.html +++ /dev/null @@ -1,982 +0,0 @@ - - - - - - - Einladungen – xXx Sphere - - - - - -
-
-

Einladungen

- -
- - -
- - -
-
- - -
- - -
-
- - -
-
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- -
-
🎲
-
-
-
Vanilla Game – Einladung
-
-
-

- Du wurdest zu einem Vanilla Game eingeladen. Wie möchtest du mitspielen? -

-
-
- - - -
-
-
- - -
-
-
- -
-
⛓️
-
-
-
BDSM Game – Einladung
-
-
-

- Du wurdest zu einem BDSM Game eingeladen. Wie möchtest du mitspielen? -

-
-
- - - -
-
-
- - -
-
-
- -
-
🔒
-
-
-
-
-
-
-
-
-
- - - Ziffern -
-
-
-
- - -
-
-
- - -
-
-
-
🔒
-

Dein Entsperrcode

-

- Stelle die Kombination deines Tresors auf den folgenden Code ein und verschließe deinen Schlüssel in diesem. -

-
- - -
-
- - - - - - - - diff --git a/src/main/resources/static/games/common/aufgaben.html b/src/main/resources/static/games/vanilla/aufgaben.html similarity index 96% rename from src/main/resources/static/games/common/aufgaben.html rename to src/main/resources/static/games/vanilla/aufgaben.html index 65d97b6..ec355e0 100644 --- a/src/main/resources/static/games/common/aufgaben.html +++ b/src/main/resources/static/games/vanilla/aufgaben.html @@ -4,7 +4,7 @@ - Aufgaben – xXx Sphere + Aufgaben – Vanilla – xXx Sphere + + + +
+
+ +
Wird geladen…
+
+ +
+
+ + + + + + diff --git a/src/main/resources/static/games/vanilla/neuvanilla.html b/src/main/resources/static/games/vanilla/neuvanilla.html index 027f413..559bed4 100644 --- a/src/main/resources/static/games/vanilla/neuvanilla.html +++ b/src/main/resources/static/games/vanilla/neuvanilla.html @@ -191,7 +191,7 @@

- Gruppen verwalten: Aufgaben-Verwaltung (Vanilla) + Gruppen verwalten: Aufgaben-Verwaltung (Vanilla)

diff --git a/src/main/resources/static/games/vanilla/toys.html b/src/main/resources/static/games/vanilla/toys.html new file mode 100644 index 0000000..a2b3e67 --- /dev/null +++ b/src/main/resources/static/games/vanilla/toys.html @@ -0,0 +1,642 @@ + + + + + + + Toys – xXx Sphere + + + + + + + + + +
+
+ + +
+
+

Meine Toys

+
+ + + +
+
+
+
+ +
+
+ + +
+
+

System-Toys

+
+ +
+
+
+
+ +
+
+ +
+
+ + + + + + diff --git a/src/main/resources/static/img/card_old.png b/src/main/resources/static/img/card_old.png deleted file mode 100644 index b3e1068..0000000 Binary files a/src/main/resources/static/img/card_old.png and /dev/null differ diff --git a/src/main/resources/static/js/mobile-nav.js b/src/main/resources/static/js/mobile-nav.js new file mode 100644 index 0000000..6fab66a --- /dev/null +++ b/src/main/resources/static/js/mobile-nav.js @@ -0,0 +1,459 @@ +(function () { + if (window.__mobileNavLoaded) return; + window.__mobileNavLoaded = true; + + const path = window.location.pathname; + const I = window.IC || function () { return ''; }; + const TOPBAR_H = '4.875rem'; + + // ── CSS ────────────────────────────────────────────────────────────────── + const style = document.createElement('style'); + style.textContent = ` + .mobile-topbar { + display: none; + position: fixed; + top: 0; left: 0; right: 0; + height: ${TOPBAR_H}; + background: var(--color-card); + border-bottom: 1px solid var(--color-secondary); + box-shadow: 0 2px 12px rgba(0,0,0,0.4); + z-index: 500; + align-items: center; + padding: 0 0.25rem; + } + .mobile-topbar-logo { + position: absolute; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + pointer-events: auto; + } + .mobile-topbar-logo img { + height: 3rem; + width: auto; + } + .mobile-topbar-actions { + display: flex; + align-items: center; + margin-left: auto; + } + .mobile-tb-btn { + position: relative; + background: none; + border: none; + color: var(--color-text); + font-size: 1.725rem; + line-height: 1; + padding: 0.75rem 0.675rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + border-radius: 12px; + text-decoration: none; + transition: background 0.12s; + flex-shrink: 0; + } + .mobile-tb-btn:hover { background: var(--color-secondary); } + .mobile-tb-badge { + position: absolute; + top: 3px; right: 3px; + background: var(--color-primary); + color: #fff; + border-radius: 10px; + font-size: 0.87rem; + font-weight: 700; + min-width: 1.65em; + padding: 0.15em 0.375em; + display: none; + line-height: 1.4; + text-align: center; + } + .mobile-tb-avatar { + width: 2.7rem; + height: 2.7rem; + border-radius: 50%; + object-fit: cover; + } + + /* ── Mobile Menu Backdrop ── */ + .mob-menu-backdrop { + display: none; + position: fixed; + top: ${TOPBAR_H}; + left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.55); + z-index: 998; + } + .mob-menu-backdrop.open { display: block; } + + /* ── Mobile Menu Panel ── */ + .mob-menu-panel { + position: fixed; + top: ${TOPBAR_H}; + right: 0; + bottom: 0; + width: min(80%, 360px); + background: var(--color-card); + border-left: 1px solid var(--color-secondary); + z-index: 999; + overflow-y: auto; + transform: translateX(100%); + transition: transform 0.25s ease; + } + .mob-menu-panel.open { transform: translateX(0); } + + /* ── Accordion ── */ + .mnav-section { border-bottom: 1px solid var(--color-secondary); } + .mnav-section:last-child { border-bottom: none; } + + .mnav-section-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.85rem 1.1rem; + cursor: pointer; + user-select: none; + font-size: 0.88rem; + font-weight: 600; + color: var(--color-text); + transition: background 0.12s; + } + .mnav-section-header:hover { background: var(--color-secondary); } + + .mnav-section-arrow { + font-size: 0.65rem; + transition: transform 0.2s; + color: var(--color-muted); + } + .mnav-section.open .mnav-section-arrow { transform: rotate(90deg); } + + .mnav-section-body { display: none; } + .mnav-section.open .mnav-section-body { display: block; } + + /* ── Menu Links ── */ + .mnav-link { + display: flex; + align-items: center; + gap: 0.65rem; + padding: 0.6rem 1.1rem 0.6rem 1.5rem; + text-decoration: none; + color: var(--color-text); + font-size: 0.9rem; + transition: background 0.12s, color 0.12s; + } + .mnav-link:hover { background: var(--color-secondary); color: var(--color-primary); } + .mnav-link.active { color: var(--color-primary); background: rgba(var(--color-primary-rgb,233,69,96),0.08); } + .mnav-icon { width: 1.3rem; text-align: center; font-size: 1rem; flex-shrink: 0; } + .mnav-badge { + margin-left: auto; + background: var(--color-primary); color: #fff; + border-radius: 10px; font-size: 0.68rem; font-weight: 700; + min-width: 1.2em; padding: 0.1em 0.3em; display: none; + } + .mnav-link--danger { color: var(--color-primary); } + .mnav-link--danger:hover { background: rgba(var(--color-primary-rgb,233,69,96),0.1); color: var(--color-primary); } + + /* ── Show only on mobile ── */ + @media (max-width: 768px) { + .mobile-topbar { display: flex; } + body.app { padding-top: ${TOPBAR_H}; } + } + `; + document.head.appendChild(style); + + // ── Helper ─────────────────────────────────────────────────────────────── + function lnk(href, iconKey, label, id, badgeId) { + const cls = path === href ? ' active' : ''; + const idAttr = id ? ` id="${id}"` : ''; + const badge = badgeId ? `` : ''; + const icon = iconKey ? `${I(iconKey) || ''}` : ``; + return `${icon}${label}${badge}`; + } + + // ── Menu-Sektionen ──────────────────────────────────────────────────────── + const SECTIONS = [ + { + label: 'Allgemein', + prefixes: ['/userhome.html', '/search.html', '/community/nachrichten.html', + '/community/benachrichtigungen.html', '/games/common/einladungen.html'], + html: ` + ${lnk('/userhome.html', 'HOME', 'Home' )} + ${lnk('/search.html', 'SEARCH', 'Suche' )} + + ${lnk('/community/nachrichten.html', 'MESSAGES', 'Nachrichten', null, 'mnavBadgeMsg' )} + ${lnk('/community/benachrichtigungen.html', 'NOTIFICATIONS', 'Benachrichtigungen', null, 'mnavBadgeNotif' )} + ${lnk('/games/common/einladungen.html', 'INVITATIONS', 'Einladungen', null, 'mnavBadgeInv' )} + `, + }, + { + label: 'Community', + prefixes: ['/community/'], + html: ` + ${lnk('/community/feed.html', 'FEED', 'Feed' )} + ${lnk('/community/freunde.html', 'FRIENDS', 'Freunde', null, 'mnavBadgeFriends' )} + ${lnk('/community/gruppen.html', 'GROUPS', 'Gruppen', null, 'mnavBadgeGruppen' )} + ${lnk('/community/locations.html', 'LOCATION', 'Locations' )} + ${lnk('/community/events.html', 'EVENT', 'Veranstaltungen' )} + `, + }, + { + label: 'Dating', + prefixes: ['/dating/'], + html: ` + + ${I('DATING') || '♥'}Dating + ${lnk('/dating/besucher.html', '', 'Besucher')} + ${lnk('/dating/likes.html', '', 'Likes' )} + ${lnk('/dating/matches.html', '', 'Matches' )} + `, + }, + { + label: 'Vanilla Game', + prefixes: ['/games/vanilla/'], + html: ` + ${lnk('/games/vanilla/neuvanilla.html', 'PLAY_NEW', 'Neue Session', 'mnavVanillaNeu' )} + + + ${lnk('/games/vanilla/aufgaben.html', 'CHECK', 'Aufgaben' )} + ${lnk('/games/vanilla/toys.html', 'TOYS', 'Toys' )} + ${lnk('/games/vanilla/entdecken.html', 'DISCOVER', 'Entdecken' )} + `, + }, + { + label: 'BDSM Game', + prefixes: ['/games/bdsm/'], + html: ` + ${lnk('/games/bdsm/neubdsm.html', 'PLAY_NEW', 'Neue Session', 'mnavBdsmNeu' )} + + + ${lnk('/games/bdsm/aufgaben.html', 'CHECK', 'Aufgaben' )} + ${lnk('/games/bdsm/toys.html', 'TOYS', 'Toys' )} + ${lnk('/games/bdsm/entdecken.html', 'DISCOVER', 'Entdecken' )} + `, + }, + { + label: 'Chastity Game', + prefixes: ['/games/chastity/'], + html: ` + ${lnk('/games/chastity/neulock.html', 'NEW_LOCK', 'Neues Lock', 'mnavChastityNeu' )} + + ${lnk('/games/chastity/communityvotes.html', 'VOTES', 'Community Votes' )} + ${lnk('/games/chastity/meine-locks.html', 'LOCK', 'Meine Vorlagen' )} + ${lnk('/games/chastity/entdecken-vorlagen.html', 'DISCOVER', 'Entdecken' )} + ${lnk('/games/chastity/keyholder-finden.html', 'FRIENDS', 'Keyholder finden' )} + ${lnk('/games/chastity/keyholder.html', 'KEY', 'Keyholder' )} + ${lnk('/games/chastity/unlock-history.html', 'HISTORY', 'Code-Historie' )} + `, + }, + { + label: 'Konto', + prefixes: ['/konto/', '/help/'], + html: ` + ${lnk('/konto/einstellungen.html', 'SETTINGS', 'Einstellungen')} + ${lnk('/help/overview.html', 'HELP', 'Hilfe' )} + + ${I('LOGOUT') || ''}Abmelden + `, + }, + ]; + + // ── Mobile Topbar ───────────────────────────────────────────────────────── + const topbarEl = document.createElement('div'); + topbarEl.className = 'mobile-topbar'; + topbarEl.id = 'mobileTopbar'; + topbarEl.innerHTML = ` + + + `; + document.body.insertAdjacentElement('afterbegin', topbarEl); + + // ── Overlay ─────────────────────────────────────────────────────────────── + const backdropEl = document.createElement('div'); + backdropEl.className = 'mob-menu-backdrop'; + backdropEl.id = 'mobMenuBackdrop'; + + const panelEl = document.createElement('div'); + panelEl.className = 'mob-menu-panel'; + panelEl.id = 'mobMenuPanel'; + panelEl.innerHTML = SECTIONS.map(s => { + const isOpen = s.prefixes.some(p => path.startsWith(p) || path === p); + return ` + `; + }).join(''); + + document.body.appendChild(backdropEl); + document.body.appendChild(panelEl); + + // ── Accordion (nur eine Sektion gleichzeitig offen) ────────────────────── + panelEl.querySelectorAll('.mnav-section-header').forEach(h => { + h.addEventListener('click', () => { + const section = h.closest('.mnav-section'); + const isOpen = section.classList.contains('open'); + panelEl.querySelectorAll('.mnav-section').forEach(s => s.classList.remove('open')); + if (!isOpen) section.classList.add('open'); + }); + }); + + // ── Open / Close ────────────────────────────────────────────────────────── + function openMenu() { + panelEl.classList.add('open'); + backdropEl.classList.add('open'); + } + function closeMenu() { + panelEl.classList.remove('open'); + backdropEl.classList.remove('open'); + } + + document.getElementById('mobMenuToggle').addEventListener('click', e => { + e.stopPropagation(); + panelEl.classList.contains('open') ? closeMenu() : openMenu(); + }); + backdropEl.addEventListener('click', closeMenu); + document.addEventListener('keydown', e => { if (e.key === 'Escape') closeMenu(); }); + panelEl.querySelectorAll('.mnav-link').forEach(l => { + l.addEventListener('click', () => { if (l.getAttribute('href') !== '#') closeMenu(); }); + }); + + // ── Badges ──────────────────────────────────────────────────────────────── + function setBadge(id, n) { + const el = document.getElementById(id); + if (!el) return; + el.textContent = n > 99 ? '99+' : n; + el.style.display = n > 0 ? 'inline-block' : 'none'; + } + + fetch('/social/messages/unread/count').then(r => r.ok ? r.json() : 0).then(n => { + setBadge('mobTbMsgBadge', n); setBadge('mnavBadgeMsg', n); + }).catch(() => {}); + + fetch('/notifications/unread/count').then(r => r.ok ? r.json() : 0).then(n => { + setBadge('mobTbNotifBadge', n); setBadge('mnavBadgeNotif', n); + }).catch(() => {}); + + Promise.all([ + fetch('/lockee/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/keyholder/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/bdsm/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/vanilla/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + ]).then(([l, k, b, v]) => { + const n = l + k + b + v; + setBadge('mobTbInvBadge', n); setBadge('mnavBadgeInv', n); + }).catch(() => {}); + + fetch('/social/friends/pending/count').then(r => r.ok ? r.json() : 0) + .then(n => setBadge('mnavBadgeFriends', n)).catch(() => {}); + + Promise.all([ + fetch('/gruppen/requests/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + fetch('/gruppen/reports/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), + ]).then(([j, rep]) => setBadge('mnavBadgeGruppen', j + rep)).catch(() => {}); + + // ── User / Dynamische Links ─────────────────────────────────────────────── + fetch('/login/me').then(r => r.ok ? r.json() : null).then(async user => { + if (!user) return; + + // Profilbild + const profileBtn = document.getElementById('mobTbProfileBtn'); + if (profileBtn) { + profileBtn.href = '/community/benutzer.html?userId=' + user.userId; + if (user.profilePicture) { + profileBtn.innerHTML = + ``; + } + } + + // Admin + if (user.admin) { + const el = document.getElementById('mnavAdminLink'); + if (el) el.style.display = ''; + } + + // Dating + const datingLink = document.getElementById('mnavDatingLink'); + if (datingLink) { + datingLink.href = user.datingAktiv + ? '/dating/dating.html' + : '/konto/einstellungen.html#sec-dating'; + } + + const hide = id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; }; + const show = id => { const el = document.getElementById(id); if (el) el.style.display = ''; }; + const setHref = (id, h) => { const el = document.getElementById(id); if (el) el.href = h; }; + + // BDSM + try { + const r = await fetch('/bdsm/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('mnavBdsmNeu'); hide('mnavBdsmImSpiel'); show('mnavBdsmAktiv'); + setHref('mnavBdsmAktiv', aktiv.sessionId ? '/games/bdsm/bdsmingame.html' : '/games/bdsm/neubdsm.html'); + } else { + const sr = await fetch(`/bdsm?userId=${user.userId}`); + if (sr.status === 200) { hide('mnavBdsmNeu'); show('mnavBdsmImSpiel'); } + else show('mnavBdsmNeu'); + } + } catch (_) {} + + // Vanilla + try { + const r = await fetch('/vanilla/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('mnavVanillaNeu'); hide('mnavVanillaImSpiel'); show('mnavVanillaAktiv'); + setHref('mnavVanillaAktiv', aktiv.sessionId ? '/games/vanilla/vanillaingame.html' : '/games/vanilla/neuvanilla.html'); + } else { + const sr = await fetch(`/vanilla?userId=${user.userId}`); + if (sr.status === 200) { hide('mnavVanillaNeu'); show('mnavVanillaImSpiel'); } + else show('mnavVanillaNeu'); + } + } catch (_) {} + + // Chastity + try { + const r = await fetch('/keyholder/mylock'); + if (r.ok) { + const lock = await r.json(); + show('mnavChastityAktiv'); + setHref('mnavChastityAktiv', '/games/chastity/activelock.html?lockId=' + lock.lockId); + } + } catch (_) {} + }).catch(() => {}); +})(); diff --git a/src/main/resources/static/js/nav.js b/src/main/resources/static/js/nav.js index 4073704..70fa159 100644 --- a/src/main/resources/static/js/nav.js +++ b/src/main/resources/static/js/nav.js @@ -8,23 +8,48 @@ /* ── Burger-Button ── */ .nav-burger { display: inline-flex; align-items: center; gap: 0.45rem; - padding: 0.35rem 0.8rem 0.35rem 0.6rem; + padding: 0.46rem 0.8rem 0.46rem 0.6rem; background: none; border: 1px solid var(--color-secondary); border-radius: 8px; cursor: pointer; color: var(--color-text); font-size: 0.88rem; font-weight: 600; flex-shrink: 0; transition: border-color 0.15s, color 0.15s; margin-right: 0.5rem; + line-height: 1; } .nav-burger:hover { border-color: var(--color-primary); color: var(--color-primary); } - .nav-burger-icon { font-size: 1.05rem; line-height: 1; } + .nav-burger-icon { + font-size: 1.05rem; line-height: 1; + position: relative; + display: inline-flex; align-items: center; justify-content: center; + width: 1.2em; height: 1.2em; + } + .nav-burger-icon-menu, + .nav-burger-icon-close { + position: absolute; + inset: 0; + display: flex; align-items: center; justify-content: center; + transition: opacity 0.15s, transform 0.15s; + } + .nav-burger-icon-close { opacity: 0; transform: rotate(-45deg); } + #topbar.nav-menu-open .nav-burger .nav-burger-icon-menu { opacity: 0; transform: rotate(45deg); } + #topbar.nav-menu-open .nav-burger .nav-burger-icon-close { opacity: 1; transform: rotate(0deg); } /* ── Backdrop ── */ .nav-backdrop { - display: none; position: fixed; inset: 0; z-index: 498; + position: fixed; inset: 0; z-index: 498; + background: rgba(0,0,0,0); + pointer-events: none; + transition: background 0.2s ease; + } + .nav-backdrop.open { + background: rgba(0,0,0,0.55); + pointer-events: auto; } - .nav-backdrop.open { display: block; } /* ── Topbar: untere Rundung entfernen wenn Menü offen ── */ + #topbar { + transition: border-radius 0.22s ease, border-bottom-color 0.22s ease; + } #topbar.nav-menu-open { border-radius: 12px 12px 0 0; border-bottom-color: transparent; @@ -38,11 +63,21 @@ border-top: none; border-radius: 0 0 12px 12px; box-shadow: 0 12px 40px rgba(0,0,0,0.55); - display: none; overflow-y: auto; max-height: calc(100vh - 80px); + visibility: hidden; + opacity: 0; + transform: translateY(-12px); + pointer-events: none; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s; + } + .nav-dropdown.open { + visibility: visible; + opacity: 1; + transform: translateY(0); + pointer-events: auto; + transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0s; } - .nav-dropdown.open { display: block; } /* ── 4-Spalten-Layout ── */ .nav-columns { @@ -226,17 +261,17 @@ { href: '/games/vanilla/neuvanilla.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navVanillaNeu' }, { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navVanillaAktiv' }, { href: '/games/vanilla/vanillaingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navVanillaImSpiel' }, - { href: '/games/common/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: 'TOYS', label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + { href: '/games/vanilla/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/vanilla/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/vanilla/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, ])} ${gameGroup('BDSM', 'BDSM Game', [ - { href: '/games/bdsm/neubdsm.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navBdsmNeu' }, - { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navBdsmAktiv' }, - { href: '/games/bdsm/bdsmingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navBdsmImSpiel' }, - { href: '/games/common/aufgaben.html?mode=bdsm', icon: 'CHECK', label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: 'TOYS', label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + { href: '/games/bdsm/neubdsm.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navBdsmNeu' }, + { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navBdsmAktiv' }, + { href: '/games/bdsm/bdsmingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navBdsmImSpiel' }, + { href: '/games/bdsm/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/bdsm/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/bdsm/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, ])} ${gameGroup('CHASTITY', 'Chastity Game', [ { href: '/games/chastity/neulock.html', icon: 'NEW_LOCK', label: 'Neues Lock', id: 'navChastityNeu' }, @@ -330,8 +365,8 @@ btn.className = 'nav-burger'; btn.id = 'navBurgerBtn'; btn.setAttribute('aria-label', 'Menü öffnen'); - btn.innerHTML = `${I('MENU') || '☰'}Menü`; - topbarLeft.prepend(btn); + btn.innerHTML = `${I('MENU') || '≡'}${I('CLOSE') || 'x'}Menü`; + topbarLeft.append(btn); btn.addEventListener('click', e => { e.stopPropagation(); const dd = document.getElementById('navDropdown'); @@ -460,4 +495,6 @@ } loadScript('/js/topbar.js'); loadScript('/js/social-sidebar.js'); + loadScript('/js/section-nav.js'); + loadScript('/js/mobile-nav.js'); })(); diff --git a/src/main/resources/static/js/section-nav.js b/src/main/resources/static/js/section-nav.js new file mode 100644 index 0000000..4328936 --- /dev/null +++ b/src/main/resources/static/js/section-nav.js @@ -0,0 +1,243 @@ +(function () { + const path = window.location.pathname; + const search = window.location.search; + const I = window.IC || function () { return ''; }; + + // ── Bereichs-Definitionen ──────────────────────────────────────────────── + const SECTIONS = { + social: { + prefixes: ['/community/'], + exclude: [ + '/community/nachrichten.html', + '/community/benachrichtigungen.html', + '/community/einladungen.html', + ], + items: [ + { href: '/community/feed.html', icon: 'FEED', label: 'Feed' }, + { href: '/community/freunde.html', icon: 'FRIENDS', label: 'Freunde' }, + { href: '/community/gruppen.html', icon: 'GROUPS', label: 'Gruppen' }, + { href: '/community/locations.html', icon: 'LOCATION', label: 'Locations' }, + { href: '/community/events.html', icon: 'EVENT', label: 'Veranstaltungen' }, + ], + }, + dating: { + prefixes: ['/dating/'], + items: [ + { href: '/dating/dating.html', icon: 'DATING', label: 'Dating', id: 'snavDatingLink' }, + { href: '/dating/besucher.html', icon: '', label: 'Besucher' }, + { href: '/dating/likes.html', icon: '', label: 'Likes' }, + { href: '/dating/matches.html', icon: '', label: 'Matches' }, + ], + }, + vanilla: { + prefixes: ['/games/vanilla/'], + items: [ + { href: '/games/vanilla/neuvanilla.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'snavVanillaNeu' }, + { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'snavVanillaAktiv', hidden: true }, + { href: '/games/vanilla/vanillaingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'snavVanillaImSpiel', hidden: true }, + { href: '/games/vanilla/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/vanilla/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/vanilla/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + ], + }, + bdsm: { + prefixes: ['/games/bdsm/'], + items: [ + { href: '/games/bdsm/neubdsm.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'snavBdsmNeu' }, + { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'snavBdsmAktiv', hidden: true }, + { href: '/games/bdsm/bdsmingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'snavBdsmImSpiel', hidden: true }, + { href: '/games/bdsm/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, + { href: '/games/bdsm/toys.html', icon: 'TOYS', label: 'Toys' }, + { href: '/games/bdsm/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, + ], + }, + chastity: { + prefixes: ['/games/chastity/'], + items: [ + { href: '/games/chastity/neulock.html', icon: 'NEW_LOCK', label: 'Neues Lock', id: 'snavChastityNeu' }, + { href: '#', icon: 'ACTIVE_LOCK', label: 'Aktives Lock', id: 'snavChastityAktiv', hidden: true }, + { href: '/games/chastity/communityvotes.html', icon: 'VOTES', label: 'Community Votes' }, + { href: '/games/chastity/meine-locks.html', icon: 'LOCK', label: 'Meine Vorlagen' }, + { href: '/games/chastity/entdecken-vorlagen.html', icon: 'DISCOVER', label: 'Entdecken' }, + { href: '/games/chastity/keyholder-finden.html', icon: 'FRIENDS', label: 'Keyholder finden' }, + { href: '/games/chastity/keyholder.html', icon: 'KEY', label: 'Keyholder' }, + { href: '/games/chastity/unlock-history.html', icon: 'HISTORY', label: 'Code-Historie' }, + ], + }, + }; + + // ── Aktiven Bereich ermitteln ──────────────────────────────────────────── + const sectionKey = Object.keys(SECTIONS).find(k => { + const s = SECTIONS[k]; + if (!s.prefixes.some(p => path.startsWith(p))) return false; + if (s.exclude && s.exclude.includes(path)) return false; + return true; + }); + if (!sectionKey) return; + const section = SECTIONS[sectionKey]; + + // ── CSS ────────────────────────────────────────────────────────────────── + const style = document.createElement('style'); + style.textContent = ` + .section-nav { + display: flex; + align-items: center; + gap: 0.15rem; + flex-wrap: wrap; + padding: 0 0 0.6rem 0; + } + .section-nav-link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.3rem 0.75rem; + border-radius: 7px; + text-decoration: none; + color: var(--color-muted); + font-size: 0.85rem; + font-weight: 500; + white-space: nowrap; + transition: background 0.12s, color 0.12s; + } + .section-nav-link:hover { + background: var(--color-secondary); + color: var(--color-text); + } + .section-nav-link.active { + color: var(--color-primary); + background: rgba(var(--color-primary-rgb, 233,69,96), 0.09); + font-weight: 600; + } + .section-nav-icon { + font-size: 0.9rem; + line-height: 1; + flex-shrink: 0; + } + .section-nav--icons-only .section-nav-label { display: none; } + .section-nav--icons-only .section-nav-link { gap: 0; padding: 0.3rem 0.55rem; } + .section-nav-sep { + border: none; + border-top: 1px solid var(--color-secondary); + margin: 0 0 1.25rem 0; + } + `; + document.head.appendChild(style); + + // ── Aktiv-Erkennung ────────────────────────────────────────────────────── + function isActive(item) { + if (item.href === '#') return false; + const [itemPath, itemQuery] = item.href.split('?'); + if (itemQuery) return path === itemPath && search === '?' + itemQuery; + return path === itemPath; + } + + // ── Nav bauen ──────────────────────────────────────────────────────────── + const navEl = document.createElement('nav'); + navEl.className = 'section-nav'; + + section.items.forEach(item => { + const a = document.createElement('a'); + a.href = item.href; + a.className = 'section-nav-link' + (isActive(item) ? ' active' : ''); + if (item.id) a.id = item.id; + if (item.hidden) a.style.display = 'none'; + a.title = item.label; + if (item.icon) a.innerHTML += `${I(item.icon) || ''}`; + a.innerHTML += `${item.label}`; + navEl.appendChild(a); + }); + + const sep = document.createElement('hr'); + sep.className = 'section-nav-sep'; + + // ── Einfügen in .main ──────────────────────────────────────────────────── + function checkOverflow() { + // Immer zuerst Labels einblenden und ohne wrap messen — + // so gibt es keine Feedback-Schleife durch Größenänderung nach dem Umschalten + navEl.classList.remove('section-nav--icons-only'); + navEl.style.flexWrap = 'nowrap'; + const overflows = navEl.scrollWidth > navEl.clientWidth; + navEl.style.flexWrap = ''; + if (overflows) navEl.classList.add('section-nav--icons-only'); + } + + function inject() { + const main = document.querySelector('.main'); + if (!main) { setTimeout(inject, 30); return; } + main.insertBefore(sep, main.firstChild); + main.insertBefore(navEl, sep); + loadDynamic(); + // Overflow-Erkennung beim Laden und bei Größenänderung + requestAnimationFrame(checkOverflow); + new ResizeObserver(checkOverflow).observe(navEl); + } + inject(); + + // ── Dynamische Elemente (analog nav.js) ────────────────────────────────── + function hide(id) { const el = document.getElementById(id); if (el) el.style.display = 'none'; } + function show(id) { const el = document.getElementById(id); if (el) el.style.display = ''; } + function setHref(id, h) { const el = document.getElementById(id); if (el) el.href = h; } + + async function loadDynamic() { + try { + const res = await fetch('/login/me'); + if (!res.ok) return; + const user = await res.json(); + if (!user) return; + + // Dating-Link + const datingLink = document.getElementById('snavDatingLink'); + if (datingLink) { + datingLink.href = user.datingAktiv + ? '/dating/dating.html' + : '/konto/einstellungen.html#sec-dating'; + } + + // BDSM + if (sectionKey === 'bdsm') { + try { + const r = await fetch('/bdsm/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('snavBdsmNeu'); hide('snavBdsmImSpiel'); + show('snavBdsmAktiv'); + setHref('snavBdsmAktiv', aktiv.sessionId ? '/games/bdsm/bdsmingame.html' : '/games/bdsm/neubdsm.html'); + } else { + const sr = await fetch(`/bdsm?userId=${user.userId}`); + if (sr.status === 200) { hide('snavBdsmNeu'); show('snavBdsmImSpiel'); } + else show('snavBdsmNeu'); + } + } catch (_) { show('snavBdsmNeu'); } + } + + // Vanilla + if (sectionKey === 'vanilla') { + try { + const r = await fetch('/vanilla/einladung/meine-aktive'); + if (r.ok) { + const aktiv = await r.json(); + hide('snavVanillaNeu'); hide('snavVanillaImSpiel'); + show('snavVanillaAktiv'); + setHref('snavVanillaAktiv', aktiv.sessionId ? '/games/vanilla/vanillaingame.html' : '/games/vanilla/neuvanilla.html'); + } else { + const sr = await fetch(`/vanilla?userId=${user.userId}`); + if (sr.status === 200) { hide('snavVanillaNeu'); show('snavVanillaImSpiel'); } + else show('snavVanillaNeu'); + } + } catch (_) { show('snavVanillaNeu'); } + } + + // Chastity + if (sectionKey === 'chastity') { + try { + const r = await fetch('/keyholder/mylock'); + if (r.ok) { + const lock = await r.json(); + show('snavChastityAktiv'); + setHref('snavChastityAktiv', '/games/chastity/activelock.html?lockId=' + lock.lockId); + } + } catch (_) {} + } + } catch (_) {} + } +})(); diff --git a/src/main/resources/static/js/sidebar.js b/src/main/resources/static/js/sidebar.js index 2ddd7bf..80ea841 100644 --- a/src/main/resources/static/js/sidebar.js +++ b/src/main/resources/static/js/sidebar.js @@ -10,9 +10,9 @@ { href: '/games/vanilla/neuvanilla.html', icon: I('PLAY_NEW'), label: 'Neue Session', id: 'navVanillaNeu' }, { href: '#', icon: I('WAITING'), label: 'Aktive Session', id: 'navVanillaAktiv' }, { href: '/games/vanilla/vanillaingame.html', icon: I('PLAY_ACTIVE'), label: 'Im Spiel', id: 'navVanillaImSpiel' }, - { href: '/games/common/aufgaben.html', icon: I('CHECK'), label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: I('TOYS'), label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, + { href: '/games/vanilla/aufgaben.html', icon: I('CHECK'), label: 'Aufgaben' }, + { href: '/games/vanilla/toys.html', icon: I('TOYS'), label: 'Toys' }, + { href: '/games/vanilla/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, ] }, { @@ -22,9 +22,9 @@ { href: '/games/bdsm/neubdsm.html', icon: I('PLAY_NEW'), label: 'Neue Session', id: 'navBdsmNeu' }, { href: '#', icon: I('WAITING'), label: 'Aktive Session', id: 'navBdsmAktiv' }, { href: '/games/bdsm/bdsmingame.html', icon: I('PLAY_ACTIVE'), label: 'Im Spiel', id: 'navBdsmImSpiel' }, - { href: '/games/common/aufgaben.html?mode=bdsm', icon: I('CHECK'), label: 'Aufgaben' }, - { href: '/games/common/toys.html', icon: I('TOYS'), label: 'Toys' }, - { href: '/games/chastity/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, + { href: '/games/bdsm/aufgaben.html', icon: I('CHECK'), label: 'Aufgaben' }, + { href: '/games/bdsm/toys.html', icon: I('TOYS'), label: 'Toys' }, + { href: '/games/bdsm/entdecken.html', icon: I('DISCOVER'), label: 'Entdecken' }, ] }, { diff --git a/src/main/resources/static/search.html b/src/main/resources/static/search.html index 3f0b2d5..5d193a1 100644 --- a/src/main/resources/static/search.html +++ b/src/main/resources/static/search.html @@ -165,7 +165,7 @@ .search-load-more:hover { border-color: var(--color-primary); color: var(--color-primary); background: none; } - +

Suche