Menp überarbeitet
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
This commit is contained in:
@@ -740,7 +740,7 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script src="/js/meldung.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -33,6 +33,6 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
function fmtRelTime(isoStr) {
|
||||
|
||||
@@ -652,7 +652,7 @@
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/image-viewer.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script src="/js/meldung.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const params = new URLSearchParams(location.search);
|
||||
const eventId = params.get('id');
|
||||
|
||||
@@ -282,7 +282,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
// ── State ─────────────────────────────────────────────────────────────────────
|
||||
const BATCH = 20;
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script src="/js/meldung.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
function switchTab(name, btn) {
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
// ── Generic modal helpers ──
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
function switchTab(name, btn) {
|
||||
|
||||
@@ -225,7 +225,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const params = new URLSearchParams(location.search);
|
||||
const locationId = params.get('id');
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
// ── Bild-Resize ──────────────────────────────────────────────────────────────
|
||||
function resizeImage(file, maxPx, quality) {
|
||||
|
||||
@@ -319,7 +319,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
const SUPPORT_USER_ID = 'dbf1e35a-e331-3211-9889-d0d21f386028';
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
let debounceTimer;
|
||||
|
||||
@@ -689,6 +689,30 @@ body.app {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.topbar-search-section {
|
||||
padding: 0.35rem 1rem 0.2rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--color-muted);
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
.topbar-search-section:first-child { border-top: none; }
|
||||
|
||||
.topbar-search-all {
|
||||
display: block;
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.topbar-search-all:hover { background: var(--color-secondary); }
|
||||
|
||||
/* ── Rechter Bereich ── */
|
||||
.topbar-right {
|
||||
display: flex;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const BATCH = 12;
|
||||
let allEntries = [];
|
||||
|
||||
@@ -1209,7 +1209,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
@@ -1271,9 +1271,18 @@
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.set('tab', tabName);
|
||||
history.replaceState(null, '', url);
|
||||
if (tabName === 'match' && !discoveryLoaded) {
|
||||
discoveryLoaded = true;
|
||||
loadDiscovery();
|
||||
if (tabName === 'match') {
|
||||
if (!discoveryLoaded) {
|
||||
discoveryLoaded = true;
|
||||
loadDiscovery();
|
||||
} else {
|
||||
const cardVisible = document.getElementById('discoveryCard').style.display !== 'none';
|
||||
const emptyVisible = document.getElementById('discoveryEmpty').style.display !== 'none';
|
||||
const isLoading = document.getElementById('discoveryLoading').style.display !== 'none';
|
||||
if (!cardVisible && !emptyVisible && !isLoading) {
|
||||
loadDiscovery();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tabName === 'entdecken' && !entdeckenLoaded) {
|
||||
entdeckenLoaded = true;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const BATCH = 12;
|
||||
let allEntries = []; // { userId } – nur für Premium; gesamt inkl. locked
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const BATCH = 12;
|
||||
let allEntries = [];
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const params = new URLSearchParams(location.search);
|
||||
const einladungId = params.get('id');
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const game = JSON.parse(sessionStorage.getItem('bdsm-session-game') || 'null');
|
||||
const setup = JSON.parse(sessionStorage.getItem('bdsm-session-setup') || 'null');
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
// ── Constants ──
|
||||
const SPIELDAUER = [
|
||||
|
||||
@@ -795,7 +795,7 @@
|
||||
<script src="/js/card-defs.js"></script>
|
||||
<script src="/js/card-display.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@
|
||||
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
function esc(s) {
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const PAGE_SIZE = 10;
|
||||
let currentPage = 0, totalPages = 1;
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }
|
||||
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
<script src="/js/card-defs.js"></script>
|
||||
<script src="/js/card-display.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }
|
||||
|
||||
@@ -546,7 +546,7 @@
|
||||
<script src="/js/card-defs.js"></script>
|
||||
<script src="/js/card-display.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }
|
||||
|
||||
@@ -359,7 +359,7 @@
|
||||
<script src="/js/card-defs.js"></script>
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
let myUserId = null;
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const SOURCE_LABELS = {
|
||||
GREEN_CARD: 'Grüne Karte',
|
||||
|
||||
@@ -1781,6 +1781,6 @@
|
||||
|
||||
</script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -349,7 +349,7 @@
|
||||
|
||||
<script src="/js/card-defs.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const PAGE_SIZE = 12;
|
||||
let userPage = 0, userTotalPages = 1, userLoading = false;
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
// ── Constants ──
|
||||
const SPIELDAUER = [
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
const game = JSON.parse(sessionStorage.getItem('vanilla-session-game') || 'null');
|
||||
const setup = JSON.parse(sessionStorage.getItem('vanilla-session-setup') || 'null');
|
||||
|
||||
@@ -103,6 +103,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
// Name vorausfüllen
|
||||
fetch('/login/me')
|
||||
|
||||
@@ -216,6 +216,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -324,7 +324,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
function toggleSection(id) {
|
||||
document.getElementById(id).classList.toggle('open');
|
||||
|
||||
@@ -339,7 +339,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
function toggleSection(id) {
|
||||
document.getElementById(id).classList.toggle('open');
|
||||
|
||||
463
src/main/resources/static/js/nav.js
Normal file
463
src/main/resources/static/js/nav.js
Normal file
@@ -0,0 +1,463 @@
|
||||
(function () {
|
||||
const path = window.location.pathname;
|
||||
const I = window.IC || function () { return ''; };
|
||||
|
||||
// ── CSS ──────────────────────────────────────────────────────────────────
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
/* ── Burger-Button ── */
|
||||
.nav-burger {
|
||||
display: inline-flex; align-items: center; gap: 0.45rem;
|
||||
padding: 0.35rem 0.8rem 0.35rem 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;
|
||||
}
|
||||
.nav-burger:hover { border-color: var(--color-primary); color: var(--color-primary); }
|
||||
.nav-burger-icon { font-size: 1.05rem; line-height: 1; }
|
||||
|
||||
/* ── Backdrop ── */
|
||||
.nav-backdrop {
|
||||
display: none; position: fixed; inset: 0; z-index: 498;
|
||||
}
|
||||
.nav-backdrop.open { display: block; }
|
||||
|
||||
/* ── Topbar: untere Rundung entfernen wenn Menü offen ── */
|
||||
#topbar.nav-menu-open {
|
||||
border-radius: 12px 12px 0 0;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
/* ── Dropdown ── */
|
||||
.nav-dropdown {
|
||||
position: fixed; z-index: 499;
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
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);
|
||||
}
|
||||
.nav-dropdown.open { display: block; }
|
||||
|
||||
/* ── 4-Spalten-Layout ── */
|
||||
.nav-columns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
.nav-col {
|
||||
border-right: 1px solid var(--color-secondary);
|
||||
min-width: 0;
|
||||
}
|
||||
.nav-col:last-child { border-right: none; }
|
||||
|
||||
/* Überschrift: auf Desktop ausgeblendet, auf Mobile als Accordion-Toggle */
|
||||
.nav-col-header {
|
||||
display: none;
|
||||
align-items: center; justify-content: space-between;
|
||||
padding: 0.75rem 1.1rem;
|
||||
font-size: 0.85rem; font-weight: 600;
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
.nav-col-arrow { font-size: 0.65rem; transition: transform 0.2s; }
|
||||
|
||||
.nav-col-body { padding: 0.35rem 0; }
|
||||
|
||||
/* ── Nav-Links ── */
|
||||
.nav-link {
|
||||
display: flex; align-items: center; gap: 0.65rem;
|
||||
padding: 0.55rem 1.1rem;
|
||||
text-decoration: none; color: var(--color-text);
|
||||
font-size: 0.9rem; font-weight: 500;
|
||||
transition: background 0.12s, color 0.12s;
|
||||
white-space: nowrap; position: relative;
|
||||
}
|
||||
.nav-link:hover { background: var(--color-secondary); color: var(--color-primary); }
|
||||
.nav-link.active { color: var(--color-primary); background: rgba(var(--color-primary-rgb,233,69,96),0.08); }
|
||||
.nav-icon { width: 1.3rem; text-align: center; font-size: 1rem; flex-shrink: 0; }
|
||||
.nav-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;
|
||||
}
|
||||
|
||||
/* ── Games: Accordion innerhalb der Spalte ── */
|
||||
.nav-game-group { }
|
||||
.nav-game-toggle {
|
||||
display: flex; align-items: center; gap: 0.65rem;
|
||||
padding: 0.55rem 1.1rem;
|
||||
cursor: pointer; font-size: 0.88rem; font-weight: 600;
|
||||
color: var(--color-text);
|
||||
transition: background 0.12s;
|
||||
user-select: none;
|
||||
}
|
||||
.nav-game-toggle:hover { background: var(--color-secondary); }
|
||||
.nav-game-icon { width: 1.3rem; text-align: center; font-size: 1rem; flex-shrink: 0; }
|
||||
.nav-game-arrow { margin-left: auto; font-size: 0.65rem; transition: transform 0.2s; color: var(--color-muted); }
|
||||
.nav-game-group.open .nav-game-arrow { transform: rotate(90deg); }
|
||||
.nav-game-sub { display: none; }
|
||||
.nav-game-group.open .nav-game-sub { display: block; }
|
||||
.nav-game-sub .nav-link { padding-left: 2.6rem; font-size: 0.85rem; }
|
||||
|
||||
/* ── Footer ── */
|
||||
.nav-dropdown-footer {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
padding: 0.55rem 1.25rem;
|
||||
display: flex; gap: 1.25rem;
|
||||
}
|
||||
.nav-dropdown-footer a {
|
||||
font-size: 0.75rem; color: var(--color-muted); text-decoration: none;
|
||||
}
|
||||
.nav-dropdown-footer a:hover { color: var(--color-primary); }
|
||||
|
||||
/* ── Responsive: Accordion-Modus (< 680px) ── */
|
||||
@media (max-width: 679px) {
|
||||
.nav-columns { grid-template-columns: 1fr; }
|
||||
|
||||
.nav-col { border-right: none; border-bottom: 1px solid var(--color-secondary); }
|
||||
.nav-col:last-child { border-bottom: none; }
|
||||
|
||||
.nav-col-header { display: flex; }
|
||||
.nav-col.col-open .nav-col-arrow { transform: rotate(90deg); }
|
||||
|
||||
.nav-col-body { display: none; padding: 0; }
|
||||
.nav-col.col-open .nav-col-body { display: block; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// ── app-wrapper ──────────────────────────────────────────────────────────
|
||||
const appWrapper = document.createElement('div');
|
||||
appWrapper.className = 'app-wrapper';
|
||||
const mainEl = document.querySelector('.main');
|
||||
if (mainEl) {
|
||||
document.body.insertBefore(appWrapper, mainEl);
|
||||
appWrapper.appendChild(mainEl);
|
||||
}
|
||||
|
||||
// ── Hilfsfunktionen ──────────────────────────────────────────────────────
|
||||
function esc(s) { return String(s ?? '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||
|
||||
function link(href, iconKey, label, id, badgeId) {
|
||||
const cls = path === href ? ' active' : '';
|
||||
const idAttr = id ? ` id="${id}"` : '';
|
||||
const badge = badgeId ? `<span class="nav-badge" id="${badgeId}"></span>` : '';
|
||||
return `<a href="${href}" class="nav-link${cls}"${idAttr}>
|
||||
<span class="nav-icon">${I(iconKey) || ''}</span>
|
||||
<span>${label}</span>${badge}
|
||||
</a>`;
|
||||
}
|
||||
|
||||
function gameGroup(iconKey, label, items) {
|
||||
const isOpen = items.some(it => it.href === path || (it.href !== '#' && path.startsWith(it.href.split('?')[0])));
|
||||
const sub = items.map(it => {
|
||||
const cls = path === it.href ? ' active' : '';
|
||||
const idAt = it.id ? ` id="${it.id}"` : '';
|
||||
return `<a href="${it.href}" class="nav-link${cls}"${idAt}>
|
||||
<span class="nav-icon">${I(it.icon) || ''}</span>
|
||||
<span>${it.label}</span>
|
||||
</a>`;
|
||||
}).join('');
|
||||
return `
|
||||
<div class="nav-game-group${isOpen ? ' open' : ''}">
|
||||
<div class="nav-game-toggle">
|
||||
<span class="nav-game-icon">${I(iconKey) || ''}</span>
|
||||
<span>${label}</span>
|
||||
<span class="nav-game-arrow">▶</span>
|
||||
</div>
|
||||
<div class="nav-game-sub">${sub}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function column(id, label, bodyHtml, pathPrefixes) {
|
||||
const isActive = pathPrefixes.some(p => path === p || path.startsWith(p));
|
||||
return `
|
||||
<div class="nav-col${isActive ? ' col-open' : ''}" id="${id}">
|
||||
<div class="nav-col-header">
|
||||
<span>${label}</span>
|
||||
<span class="nav-col-arrow">▶</span>
|
||||
</div>
|
||||
<div class="nav-col-body">${bodyHtml}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ── Spalten-Inhalt ───────────────────────────────────────────────────────
|
||||
const onDating = path.startsWith('/dating/');
|
||||
|
||||
const col1Html = `
|
||||
${link('/userhome.html', 'HOME', 'Home' )}
|
||||
${link('/search.html', 'SEARCH', 'Suche' )}
|
||||
<a href="/admin/admin.html" class="nav-link${path === '/admin/admin.html' ? ' active' : ''}"
|
||||
id="navAdminLink" style="display:none">
|
||||
<span class="nav-icon">${I('ADMIN') || '⚙'}</span>
|
||||
<span>Administration</span>
|
||||
</a>
|
||||
${link('/community/nachrichten.html', 'MESSAGES', 'Nachrichten', null, 'navBadgeMsg' )}
|
||||
${link('/community/benachrichtigungen.html', 'NOTIFICATIONS', 'Benachrichtigungen', null, 'navBadgeNotif' )}
|
||||
${link('/games/common/einladungen.html', 'INVITATIONS', 'Einladungen', null, 'navBadgeInv' )}
|
||||
`;
|
||||
|
||||
const col2Html = `
|
||||
${link('/community/feed.html', 'FEED', 'Feed' )}
|
||||
${link('/community/freunde.html', 'FRIENDS', 'Freunde', null, 'navBadgeFriends' )}
|
||||
${link('/community/gruppen.html', 'GROUPS', 'Gruppen', null, 'navBadgeGruppen' )}
|
||||
${link('/community/locations.html', 'LOCATION', 'Locations' )}
|
||||
${link('/community/events.html', 'EVENT', 'Veranstaltungen' )}
|
||||
`;
|
||||
|
||||
const col3Html = `
|
||||
<a href="/dating/dating.html" class="nav-link${onDating && path === '/dating/dating.html' ? ' active' : ''}" id="navDatingLink">
|
||||
<span class="nav-icon">${I('DATING') || '♥'}</span>
|
||||
<span>Dating</span>
|
||||
</a>
|
||||
${link('/dating/besucher.html', '', 'Besucher')}
|
||||
${link('/dating/likes.html', '', 'Likes' )}
|
||||
${link('/dating/matches.html', '', 'Matches' )}
|
||||
`;
|
||||
|
||||
const col4Html = `
|
||||
${gameGroup('VANILLA', 'Vanilla Game', [
|
||||
{ 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' },
|
||||
])}
|
||||
${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' },
|
||||
])}
|
||||
${gameGroup('CHASTITY', 'Chastity Game', [
|
||||
{ href: '/games/chastity/neulock.html', icon: 'NEW_LOCK', label: 'Neues Lock', id: 'navChastityNeu' },
|
||||
{ href: '#', icon: 'ACTIVE_LOCK', label: 'Aktives Lock', id: 'navChastityAktiv' },
|
||||
{ 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' },
|
||||
])}
|
||||
`;
|
||||
|
||||
// ── Dropdown-HTML ────────────────────────────────────────────────────────
|
||||
document.body.insertAdjacentHTML('beforeend', `
|
||||
<div class="nav-backdrop" id="navBackdrop"></div>
|
||||
<div class="nav-dropdown" id="navDropdown">
|
||||
<div class="nav-columns">
|
||||
${column('colAllgemein', 'Allgemein', col1Html, [
|
||||
'/userhome.html', '/search.html',
|
||||
'/community/nachrichten.html',
|
||||
'/community/benachrichtigungen.html',
|
||||
'/games/common/einladungen.html',
|
||||
])}
|
||||
${column('colCommunity', 'Community', col2Html, [
|
||||
'/community/feed.html', '/community/freunde.html',
|
||||
'/community/gruppen.html', '/community/gruppe.html',
|
||||
'/community/locations.html', '/community/location-detail.html',
|
||||
'/community/events.html', '/community/event-detail.html',
|
||||
'/community/abonnements.html', '/community/personen-suchen.html',
|
||||
'/community/benutzer.html',
|
||||
])}
|
||||
${column('colDating', 'Dating', col3Html, ['/dating/'])}
|
||||
${column('colGames', 'Games', col4Html, [
|
||||
'/games/vanilla/', '/games/bdsm/', '/games/chastity/',
|
||||
])}
|
||||
</div>
|
||||
<div class="nav-dropdown-footer">
|
||||
<a href="/help/kontakt.html">Kontakt & Feedback</a>
|
||||
<a href="/help/impressum.html">Impressum</a>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
// ── Dropdown positionieren ────────────────────────────────────────────────
|
||||
function positionDropdown() {
|
||||
const topbar = document.getElementById('topbar');
|
||||
const dd = document.getElementById('navDropdown');
|
||||
if (!topbar || !dd) return;
|
||||
const r = topbar.getBoundingClientRect();
|
||||
dd.style.top = r.bottom + 'px';
|
||||
dd.style.left = r.left + 'px';
|
||||
dd.style.right = (window.innerWidth - r.right) + 'px';
|
||||
dd.style.width = 'auto';
|
||||
}
|
||||
|
||||
// ── Öffnen / Schließen ────────────────────────────────────────────────────
|
||||
function openNav() {
|
||||
if (window.__topbarCloseAll) window.__topbarCloseAll();
|
||||
positionDropdown();
|
||||
document.getElementById('navDropdown').classList.add('open');
|
||||
document.getElementById('navBackdrop').classList.add('open');
|
||||
document.getElementById('topbar')?.classList.add('nav-menu-open');
|
||||
}
|
||||
function closeNav() {
|
||||
document.getElementById('navDropdown').classList.remove('open');
|
||||
document.getElementById('navBackdrop').classList.remove('open');
|
||||
document.getElementById('topbar')?.classList.remove('nav-menu-open');
|
||||
}
|
||||
// Topbar-Panels schließen das Nav-Dropdown
|
||||
window.__navClose = closeNav;
|
||||
|
||||
document.getElementById('navBackdrop').addEventListener('click', closeNav);
|
||||
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeNav(); });
|
||||
|
||||
// Links schließen das Menü
|
||||
document.getElementById('navDropdown').querySelectorAll('.nav-link').forEach(l => {
|
||||
l.addEventListener('click', () => { if (l.getAttribute('href') !== '#') closeNav(); });
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (document.getElementById('navDropdown').classList.contains('open')) positionDropdown();
|
||||
});
|
||||
|
||||
// ── Burger-Button ─────────────────────────────────────────────────────────
|
||||
function injectBurger() {
|
||||
const topbarLeft = document.querySelector('.topbar-left');
|
||||
if (!topbarLeft) { setTimeout(injectBurger, 30); return; }
|
||||
if (document.getElementById('navBurgerBtn')) return;
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'nav-burger';
|
||||
btn.id = 'navBurgerBtn';
|
||||
btn.setAttribute('aria-label', 'Menü öffnen');
|
||||
btn.innerHTML = `<span class="nav-burger-icon">${I('MENU') || '☰'}</span><span class="nav-burger-text">Menü</span>`;
|
||||
topbarLeft.prepend(btn);
|
||||
btn.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
const dd = document.getElementById('navDropdown');
|
||||
dd.classList.contains('open') ? closeNav() : openNav();
|
||||
});
|
||||
}
|
||||
setTimeout(injectBurger, 0);
|
||||
|
||||
// ── Accordion: Spalten-Header (Responsive-Modus) ──────────────────────────
|
||||
document.querySelectorAll('.nav-col .nav-col-header').forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
const col = header.closest('.nav-col');
|
||||
const isOpen = col.classList.contains('col-open');
|
||||
// alle schließen
|
||||
document.querySelectorAll('.nav-col').forEach(c => c.classList.remove('col-open'));
|
||||
if (!isOpen) col.classList.add('col-open');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Accordion: Games-Untergruppen (immer aktiv, gegenseitig exklusiv) ────
|
||||
document.querySelectorAll('.nav-game-toggle').forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
const grp = toggle.closest('.nav-game-group');
|
||||
const isOpen = grp.classList.contains('open');
|
||||
document.querySelectorAll('.nav-game-group').forEach(g => g.classList.remove('open'));
|
||||
if (!isOpen) grp.classList.add('open');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Login-abhängige Elemente ──────────────────────────────────────────────
|
||||
fetch('/login/me')
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(async user => {
|
||||
if (!user) return;
|
||||
|
||||
const datingLink = document.getElementById('navDatingLink');
|
||||
if (datingLink) {
|
||||
datingLink.href = user.datingAktiv
|
||||
? '/dating/dating.html'
|
||||
: '/konto/einstellungen.html#sec-dating';
|
||||
}
|
||||
|
||||
if (user.admin) {
|
||||
const el = document.getElementById('navAdminLink');
|
||||
if (el) el.style.display = '';
|
||||
}
|
||||
|
||||
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 href = (id, h) => { const el = document.getElementById(id); if (el) el.href = h; };
|
||||
hide('navVanillaAktiv'); hide('navVanillaImSpiel');
|
||||
hide('navBdsmAktiv'); hide('navBdsmImSpiel');
|
||||
hide('navChastityAktiv');
|
||||
|
||||
try {
|
||||
const r = await fetch('/bdsm/einladung/meine-aktive');
|
||||
if (r.ok) {
|
||||
const aktiv = await r.json();
|
||||
hide('navBdsmNeu'); hide('navBdsmImSpiel');
|
||||
show('navBdsmAktiv');
|
||||
href('navBdsmAktiv', aktiv.sessionId ? '/games/bdsm/bdsmingame.html' : '/games/bdsm/neubdsm.html');
|
||||
} else {
|
||||
const sr = await fetch(`/bdsm?userId=${user.userId}`);
|
||||
if (sr.status === 200) { hide('navBdsmNeu'); show('navBdsmImSpiel'); }
|
||||
else show('navBdsmNeu');
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
const r = await fetch('/vanilla/einladung/meine-aktive');
|
||||
if (r.ok) {
|
||||
const aktiv = await r.json();
|
||||
hide('navVanillaNeu'); hide('navVanillaImSpiel');
|
||||
show('navVanillaAktiv');
|
||||
href('navVanillaAktiv', aktiv.sessionId ? '/games/vanilla/vanillaingame.html' : '/games/vanilla/neuvanilla.html');
|
||||
} else {
|
||||
const sr = await fetch(`/vanilla?userId=${user.userId}`);
|
||||
if (sr.status === 200) { hide('navVanillaNeu'); show('navVanillaImSpiel'); }
|
||||
else show('navVanillaNeu');
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
const r = await fetch('/keyholder/mylock');
|
||||
if (r.ok) {
|
||||
const lock = await r.json();
|
||||
show('navChastityAktiv');
|
||||
href('navChastityAktiv', '/games/chastity/activelock.html?lockId=' + lock.lockId);
|
||||
}
|
||||
} catch (_) {}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
// ── Badges ───────────────────────────────────────────────────────────────
|
||||
function setBadge(id, count) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
el.textContent = count > 99 ? '99+' : count;
|
||||
el.style.display = count > 0 ? 'inline-block' : 'none';
|
||||
}
|
||||
|
||||
window.__navSetBadge = function (type, count) {
|
||||
const map = { msg: 'navBadgeMsg', notif: 'navBadgeNotif', inv: 'navBadgeInv' };
|
||||
if (map[type]) setBadge(map[type], count);
|
||||
};
|
||||
|
||||
fetch('/social/messages/unread/count').then(r => r.ok ? r.json() : 0).then(n => setBadge('navBadgeMsg', n)).catch(() => {});
|
||||
fetch('/notifications/unread/count').then(r => r.ok ? r.json() : 0).then(n => setBadge('navBadgeNotif', n)).catch(() => {});
|
||||
fetch('/social/friends/pending/count').then(r => r.ok ? r.json() : 0).then(n => setBadge('navBadgeFriends', 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('navBadgeGruppen', j + rep)).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]) => setBadge('navBadgeInv', l + k + b + v)).catch(() => {});
|
||||
|
||||
// ── Externe Scripts ───────────────────────────────────────────────────────
|
||||
function loadScript(src) {
|
||||
const s = document.createElement('script');
|
||||
s.src = src;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
loadScript('/js/topbar.js');
|
||||
loadScript('/js/social-sidebar.js');
|
||||
})();
|
||||
@@ -54,7 +54,8 @@
|
||||
const catLabel = label => `<li class="sidebar-cat-label">${label}</li>`;
|
||||
|
||||
// Home
|
||||
const homeNav = navLink({ href: '/userhome.html', icon: I('HOME') || '⌂', label: 'Home' });
|
||||
const homeNav = navLink({ href: '/userhome.html', icon: I('HOME') || '⌂', label: 'Home' });
|
||||
const searchNav = navLink({ href: '/search.html', icon: I('SEARCH') || '🔍', label: 'Suche' });
|
||||
|
||||
// Kommunikation
|
||||
const commLinks = [
|
||||
@@ -74,6 +75,7 @@
|
||||
|
||||
const socialNav = [
|
||||
homeNav,
|
||||
searchNav,
|
||||
sep,
|
||||
...commLinks.map(navLink),
|
||||
sep,
|
||||
|
||||
@@ -168,22 +168,53 @@
|
||||
|
||||
async function doSearch(q, overlay) {
|
||||
try {
|
||||
const res = await fetch('/social/users/search?q=' + encodeURIComponent(q));
|
||||
const res = await fetch('/search?q=' + encodeURIComponent(q) + '&limit=3');
|
||||
if (!res.ok) { overlay.innerHTML = '<div class="topbar-search-hint">Fehler bei der Suche.</div>'; return; }
|
||||
const users = await res.json();
|
||||
if (!users || users.length === 0) {
|
||||
const data = await res.json();
|
||||
const { users = [], locations = [], events = [] } = data;
|
||||
|
||||
if (!users.length && !locations.length && !events.length) {
|
||||
overlay.innerHTML = '<div class="topbar-search-hint">Keine Ergebnisse.</div>';
|
||||
return;
|
||||
}
|
||||
overlay.innerHTML = users.map(u => {
|
||||
const av = u.profilePicture
|
||||
? `<img src="data:image/png;base64,${esc(u.profilePicture)}" class="topbar-search-avatar" alt="">`
|
||||
: `<span class="topbar-search-avatar topbar-search-avatar--placeholder">${IC('PROFILE')}</span>`;
|
||||
return `<a href="/community/benutzer.html?userId=${esc(u.userId)}" class="topbar-search-result">
|
||||
${av}
|
||||
<span style="font-size:0.92rem;font-weight:600;">${esc(u.name)}</span>
|
||||
</a>`;
|
||||
}).join('');
|
||||
|
||||
let html = '';
|
||||
|
||||
if (users.length) {
|
||||
html += `<div class="topbar-search-section">Personen</div>`;
|
||||
html += users.map(u => {
|
||||
const av = u.profilePicture
|
||||
? `<img src="data:image/png;base64,${esc(u.profilePicture)}" class="topbar-search-avatar" alt="">`
|
||||
: `<span class="topbar-search-avatar topbar-search-avatar--placeholder">${IC('PROFILE')}</span>`;
|
||||
return `<a href="/community/benutzer.html?userId=${esc(u.userId)}" class="topbar-search-result">
|
||||
${av}<span>${esc(u.name)}</span></a>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
if (locations.length) {
|
||||
html += `<div class="topbar-search-section">Locations</div>`;
|
||||
html += locations.map(l => {
|
||||
const av = l.profilePicture
|
||||
? `<img src="data:image/png;base64,${esc(l.profilePicture)}" class="topbar-search-avatar" alt="">`
|
||||
: `<span class="topbar-search-avatar topbar-search-avatar--placeholder">📍</span>`;
|
||||
return `<a href="/community/location-detail.html?id=${esc(l.locationId)}" class="topbar-search-result">
|
||||
${av}<span>${esc(l.name)}</span></a>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
if (events.length) {
|
||||
html += `<div class="topbar-search-section">Veranstaltungen</div>`;
|
||||
html += events.map(e => {
|
||||
const av = e.imageData
|
||||
? `<img src="${esc(e.imageData)}" class="topbar-search-avatar" alt="">`
|
||||
: `<span class="topbar-search-avatar topbar-search-avatar--placeholder">🗓</span>`;
|
||||
return `<a href="/community/event-detail.html?eventId=${esc(e.eventId)}" class="topbar-search-result">
|
||||
${av}<span>${esc(e.title)}</span></a>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
html += `<a href="/search.html?q=${encodeURIComponent(q)}" class="topbar-search-all">Alle Ergebnisse anzeigen →</a>`;
|
||||
overlay.innerHTML = html;
|
||||
} catch (e) {
|
||||
overlay.innerHTML = '<div class="topbar-search-hint">Fehler bei der Suche.</div>';
|
||||
}
|
||||
@@ -207,6 +238,7 @@
|
||||
if (_activePanel === panel && panel.classList.contains('open')) {
|
||||
closeAllPanels(); return;
|
||||
}
|
||||
if (window.__navClose) window.__navClose();
|
||||
closeAllPanels();
|
||||
positionPanel(panel, btn);
|
||||
panel.classList.add('open');
|
||||
|
||||
@@ -867,7 +867,7 @@
|
||||
<div class="save-toast" id="saveToast">✓ Gespeichert</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
let myUserId = null;
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/image-viewer.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
let currentPicture = null;
|
||||
let currentPictureHq = null;
|
||||
|
||||
389
src/main/resources/static/search.html
Normal file
389
src/main/resources/static/search.html
Normal file
@@ -0,0 +1,389 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Suche – xXx Sphere</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.search-hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.search-hero-input-wrap {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.search-hero-icon {
|
||||
position: absolute;
|
||||
left: 0.85rem;
|
||||
color: var(--color-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
#searchInput {
|
||||
width: 100%;
|
||||
padding: 0.65rem 1rem 0.65rem 2.6rem;
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 8px;
|
||||
background: var(--color-card);
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#searchInput:focus { border-color: var(--color-primary); }
|
||||
|
||||
.search-tabs {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.search-tab-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 3px solid transparent;
|
||||
border-radius: 0;
|
||||
padding: 0.55rem 1.1rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-muted);
|
||||
cursor: pointer;
|
||||
margin-bottom: -1px;
|
||||
transition: color 0.15s, border-color 0.15s;
|
||||
}
|
||||
.search-tab-btn:hover { color: var(--color-text); background: none; }
|
||||
.search-tab-btn.active { color: var(--color-primary); border-bottom-color: var(--color-primary); }
|
||||
.search-tab-count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-secondary);
|
||||
color: var(--color-muted);
|
||||
border-radius: 10px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
min-width: 1.2em;
|
||||
padding: 0 0.35em;
|
||||
margin-left: 0.3em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.search-tab-btn.active .search-tab-count {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.search-panel { display: none; }
|
||||
.search-panel.active { display: block; }
|
||||
|
||||
.search-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 0.85rem;
|
||||
}
|
||||
|
||||
.search-card {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 10px;
|
||||
padding: 0.85rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
text-decoration: none;
|
||||
color: var(--color-text);
|
||||
transition: border-color 0.15s;
|
||||
min-width: 0;
|
||||
}
|
||||
.search-card:hover { border-color: var(--color-primary); }
|
||||
.search-card-avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
background: var(--color-secondary);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.3rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.search-card-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
|
||||
.search-card-avatar--square { border-radius: 8px; }
|
||||
.search-card-avatar--square img { border-radius: 6px; }
|
||||
.search-card-body { min-width: 0; }
|
||||
.search-card-name {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.search-card-sub {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.search-empty {
|
||||
text-align: center;
|
||||
padding: 2.5rem 1rem;
|
||||
color: var(--color-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.search-loading {
|
||||
text-align: center;
|
||||
padding: 2rem 1rem;
|
||||
color: var(--color-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.search-load-more {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
padding: 0.6rem;
|
||||
background: none;
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 8px;
|
||||
color: var(--color-muted);
|
||||
cursor: pointer;
|
||||
font-size: 0.88rem;
|
||||
transition: border-color 0.15s, color 0.15s;
|
||||
}
|
||||
.search-load-more:hover { border-color: var(--color-primary); color: var(--color-primary); background: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
<h1 style="margin-bottom:1rem;">Suche</h1>
|
||||
|
||||
<div class="search-hero">
|
||||
<div class="search-hero-input-wrap">
|
||||
<span class="search-hero-icon" id="searchIcon"></span>
|
||||
<input type="text" id="searchInput" placeholder="Suchen nach Personen, Locations, Veranstaltungen…"
|
||||
autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-tabs">
|
||||
<button class="search-tab-btn active" data-tab="users">
|
||||
Personen <span class="search-tab-count" id="countUsers">0</span>
|
||||
</button>
|
||||
<button class="search-tab-btn" data-tab="locations">
|
||||
Locations <span class="search-tab-count" id="countLocations">0</span>
|
||||
</button>
|
||||
<button class="search-tab-btn" data-tab="events">
|
||||
Veranstaltungen <span class="search-tab-count" id="countEvents">0</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="search-panel active" id="panel-users">
|
||||
<div class="search-loading" id="loadingUsers" style="display:none;">Wird geladen…</div>
|
||||
<div class="search-grid" id="gridUsers"></div>
|
||||
<button class="search-load-more" id="moreUsers" style="display:none;">Mehr laden</button>
|
||||
</div>
|
||||
|
||||
<div class="search-panel" id="panel-locations">
|
||||
<div class="search-loading" id="loadingLocations" style="display:none;">Wird geladen…</div>
|
||||
<div class="search-grid" id="gridLocations"></div>
|
||||
<button class="search-load-more" id="moreLocations" style="display:none;">Mehr laden</button>
|
||||
</div>
|
||||
|
||||
<div class="search-panel" id="panel-events">
|
||||
<div class="search-loading" id="loadingEvents" style="display:none;">Wird geladen…</div>
|
||||
<div class="search-grid" id="gridEvents"></div>
|
||||
<button class="search-load-more" id="moreEvents" style="display:none;">Mehr laden</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const PAGE_SIZE = 24;
|
||||
|
||||
function esc(s) {
|
||||
return String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function IC(key) { return window.IC ? window.IC(key) : (window.ICONS?.[key]?.value || ''); }
|
||||
|
||||
// ── Suchicon setzen ──
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const icon = document.getElementById('searchIcon');
|
||||
if (icon) icon.innerHTML = IC('SEARCH') || '🔍';
|
||||
});
|
||||
setTimeout(() => {
|
||||
const icon = document.getElementById('searchIcon');
|
||||
if (icon && !icon.innerHTML) icon.innerHTML = IC('SEARCH') || '🔍';
|
||||
}, 200);
|
||||
|
||||
// ── State ──
|
||||
const state = {
|
||||
users: { offset: 0, total: 0, loading: false },
|
||||
locations: { offset: 0, total: 0, loading: false },
|
||||
events: { offset: 0, total: 0, loading: false }
|
||||
};
|
||||
|
||||
let currentQuery = '';
|
||||
let debounceTimer;
|
||||
|
||||
// ── Tabs ──
|
||||
document.querySelectorAll('.search-tab-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
document.querySelectorAll('.search-tab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.querySelectorAll('.search-panel').forEach(p => p.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.getElementById('panel-' + btn.dataset.tab).classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// ── "Mehr laden" Buttons ──
|
||||
document.getElementById('moreUsers').addEventListener('click', () => loadMore('users'));
|
||||
document.getElementById('moreLocations').addEventListener('click', () => loadMore('locations'));
|
||||
document.getElementById('moreEvents').addEventListener('click', () => loadMore('events'));
|
||||
|
||||
// ── Suche starten ──
|
||||
const input = document.getElementById('searchInput');
|
||||
input.addEventListener('input', () => {
|
||||
clearTimeout(debounceTimer);
|
||||
const q = input.value.trim();
|
||||
if (q.length < 2) { clearAll(); return; }
|
||||
debounceTimer = setTimeout(() => startSearch(q), 300);
|
||||
});
|
||||
|
||||
function startSearch(q) {
|
||||
currentQuery = q;
|
||||
// URL aktualisieren
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.set('q', q);
|
||||
history.replaceState(null, '', url);
|
||||
// State zurücksetzen
|
||||
['users', 'locations', 'events'].forEach(t => {
|
||||
state[t].offset = 0;
|
||||
state[t].total = 0;
|
||||
document.getElementById('grid' + cap(t)).innerHTML = '';
|
||||
document.getElementById('more' + cap(t)).style.display = 'none';
|
||||
});
|
||||
// Alle drei Typen parallel laden
|
||||
loadChunk('users');
|
||||
loadChunk('locations');
|
||||
loadChunk('events');
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
currentQuery = '';
|
||||
['users', 'locations', 'events'].forEach(t => {
|
||||
state[t].offset = 0;
|
||||
state[t].total = 0;
|
||||
document.getElementById('grid' + cap(t)).innerHTML = '';
|
||||
document.getElementById('more' + cap(t)).style.display = 'none';
|
||||
document.getElementById('count' + cap(t)).textContent = '0';
|
||||
document.getElementById('loading' + cap(t)).style.display = 'none';
|
||||
});
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.delete('q');
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
function loadMore(type) {
|
||||
if (state[type].loading) return;
|
||||
loadChunk(type);
|
||||
}
|
||||
|
||||
async function loadChunk(type) {
|
||||
if (!currentQuery || state[type].loading) return;
|
||||
state[type].loading = true;
|
||||
|
||||
const loadingEl = document.getElementById('loading' + cap(type));
|
||||
const moreBtn = document.getElementById('more' + cap(type));
|
||||
loadingEl.style.display = '';
|
||||
moreBtn.style.display = 'none';
|
||||
|
||||
try {
|
||||
const url = `/search?q=${encodeURIComponent(currentQuery)}&limit=${PAGE_SIZE}&offset=${state[type].offset}&type=${type}`;
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error();
|
||||
const data = await res.json();
|
||||
|
||||
const items = data[type] || [];
|
||||
state[type].offset += items.length;
|
||||
state[type].total = data.total?.[type] ?? (items.length < PAGE_SIZE ? state[type].offset : state[type].offset + 1);
|
||||
|
||||
appendItems(type, items);
|
||||
|
||||
// Zähler aktualisieren
|
||||
document.getElementById('count' + cap(type)).textContent = state[type].offset;
|
||||
|
||||
// "Mehr laden" zeigen wenn noch mehr vorhanden
|
||||
moreBtn.style.display = items.length === PAGE_SIZE ? '' : 'none';
|
||||
} catch (e) {
|
||||
// ignore
|
||||
} finally {
|
||||
state[type].loading = false;
|
||||
loadingEl.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function appendItems(type, items) {
|
||||
const grid = document.getElementById('grid' + cap(type));
|
||||
if (!items.length && state[type].offset === 0) {
|
||||
if (!grid.innerHTML) {
|
||||
grid.innerHTML = `<div class="search-empty" style="grid-column:1/-1;">Keine Ergebnisse.</div>`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
items.forEach(item => {
|
||||
const card = document.createElement('a');
|
||||
if (type === 'users') {
|
||||
card.href = `/community/benutzer.html?userId=${esc(item.userId)}`;
|
||||
const av = item.profilePicture
|
||||
? `<div class="search-card-avatar"><img src="data:image/png;base64,${esc(item.profilePicture)}" alt=""></div>`
|
||||
: `<div class="search-card-avatar">${IC('PROFILE') || '👤'}</div>`;
|
||||
card.innerHTML = `${av}<div class="search-card-body"><div class="search-card-name">${esc(item.name)}</div></div>`;
|
||||
} else if (type === 'locations') {
|
||||
card.href = `/community/location-detail.html?id=${esc(item.locationId)}`;
|
||||
const av = item.profilePicture
|
||||
? `<div class="search-card-avatar search-card-avatar--square"><img src="data:image/png;base64,${esc(item.profilePicture)}" alt=""></div>`
|
||||
: `<div class="search-card-avatar search-card-avatar--square">📍</div>`;
|
||||
card.innerHTML = `${av}<div class="search-card-body"><div class="search-card-name">${esc(item.name)}</div></div>`;
|
||||
} else {
|
||||
card.href = `/community/event-detail.html?eventId=${esc(item.eventId)}`;
|
||||
const av = item.imageData
|
||||
? `<div class="search-card-avatar search-card-avatar--square"><img src="${esc(item.imageData)}" alt=""></div>`
|
||||
: `<div class="search-card-avatar search-card-avatar--square">🗓</div>`;
|
||||
const sub = item.startAt ? new Date(item.startAt).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }) : '';
|
||||
card.innerHTML = `${av}<div class="search-card-body"><div class="search-card-name">${esc(item.title)}</div><div class="search-card-sub">${esc(sub)}</div></div>`;
|
||||
}
|
||||
card.className = 'search-card';
|
||||
grid.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function cap(s) { return s.charAt(0).toUpperCase() + s.slice(1); }
|
||||
|
||||
// ── URL-Parameter beim Start auslesen ──
|
||||
const initQ = new URLSearchParams(window.location.search).get('q') || '';
|
||||
if (initQ.length >= 2) {
|
||||
input.value = initQ;
|
||||
// Warten bis icons.js geladen ist
|
||||
setTimeout(() => startSearch(initQ), 100);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -38,13 +38,13 @@
|
||||
}
|
||||
/* ── Aktivitäts-Grid (Besucher / Likes / Matches) ── */
|
||||
.activity-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
@media (max-width: 680px) {
|
||||
.activity-grid { grid-template-columns: 1fr; }
|
||||
.activity-grid { flex-direction: column; }
|
||||
}
|
||||
.activity-col {
|
||||
background: var(--color-card);
|
||||
@@ -66,11 +66,11 @@
|
||||
}
|
||||
.activity-col-link:hover { text-decoration: underline; }
|
||||
.activity-row {
|
||||
display: flex; gap: 0.5rem;
|
||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem;
|
||||
}
|
||||
/* Avatar-Karte */
|
||||
.soc-card {
|
||||
flex: 1; display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
|
||||
display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
|
||||
text-decoration: none; color: var(--color-text); cursor: pointer; min-width: 0;
|
||||
}
|
||||
.soc-card:hover .soc-avatar { border-color: var(--color-primary); }
|
||||
@@ -226,6 +226,23 @@
|
||||
.umfrage-bar-content { position:relative; display:flex; justify-content:space-between; padding:0.45rem 0.75rem; font-size:0.88rem; }
|
||||
.umfrage-total { font-size:0.78rem; color:var(--color-muted); margin-top:0.3rem; }
|
||||
|
||||
/* ── Post Lightbox ── */
|
||||
.lightbox { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.88); z-index:300; align-items:center; justify-content:center; }
|
||||
.lightbox.open { display:flex; }
|
||||
.lb-layout { display:flex; max-width:min(1340px, calc(100vw - 2rem)); width:95vw; height:min(90vh, 1100px); background:var(--color-card); border-radius:12px; overflow:hidden; position:relative; }
|
||||
.lb-post-side { flex:1; overflow-y:auto; padding:1.25rem; border-right:1px solid var(--color-secondary); min-width:0; }
|
||||
.lb-post-side .post-bild { max-height:1024px; }
|
||||
.lb-close { position:absolute; top:0.6rem; right:0.6rem; background:rgba(0,0,0,0.55); border:none; color:#fff; font-size:1.1rem; width:2rem; height:2rem; border-radius:50%; cursor:pointer; z-index:10; display:flex; align-items:center; justify-content:center; padding:0; margin:0; }
|
||||
.lb-comments-panel { width:300px; flex-shrink:0; display:flex; flex-direction:column; }
|
||||
.lb-comments-header { font-size:0.78rem; font-weight:600; color:var(--color-muted); text-transform:uppercase; letter-spacing:0.06em; padding:0.7rem 1rem; border-bottom:1px solid var(--color-secondary); flex-shrink:0; }
|
||||
.lb-comments-list { flex:1; overflow-y:auto; padding:0.75rem; }
|
||||
.lb-comment-compose { padding:0.75rem; border-top:1px solid var(--color-secondary); display:flex; flex-direction:column; gap:0.5rem; flex-shrink:0; }
|
||||
.lb-comment-compose textarea { width:100%; font-size:0.85rem; padding:0.35rem 0.6rem; resize:none; background:var(--color-secondary); border:1px solid var(--color-secondary); border-radius:6px; color:var(--color-text); font-family:inherit; outline:none; transition:border-color 0.2s; box-sizing:border-box; }
|
||||
.lb-comment-compose textarea:focus { border-color:var(--color-primary); }
|
||||
.lb-comment-compose-actions { display:flex; gap:0.5rem; justify-content:flex-end; }
|
||||
.lb-comment-compose button { width:auto; margin:0; padding:0.35rem 0.75rem; font-size:0.8rem; }
|
||||
@media (max-width:650px) { .lb-layout { flex-direction:column; height:95vh; } .lb-post-side { border-right:none; border-bottom:1px solid var(--color-secondary); max-height:55vh; } .lb-comments-panel { width:100%; } }
|
||||
|
||||
/* ── Neue Mitglieder ── */
|
||||
.new-members-strip {
|
||||
display: flex;
|
||||
@@ -385,10 +402,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Post Lightbox -->
|
||||
<div class="lightbox" id="postLightbox">
|
||||
<div class="lb-layout">
|
||||
<button class="lb-close" onclick="closeLb()">✕</button>
|
||||
<div class="lb-post-side" id="lbPostBody"></div>
|
||||
<div class="lb-comments-panel">
|
||||
<div class="lb-comments-header">Kommentare</div>
|
||||
<div class="lb-comments-list" id="lbCommentsList"></div>
|
||||
<div class="lb-comment-compose">
|
||||
<textarea id="lbCommentInput" placeholder="Kommentar schreiben…" maxlength="500" rows="3"
|
||||
onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();postLbComment()}"></textarea>
|
||||
<div class="lb-comment-compose-actions">
|
||||
<button onclick="postLbComment()">Senden</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
let myUserId = null;
|
||||
|
||||
fetch('/login/me')
|
||||
.then(r => {
|
||||
if (r.status === 401) { window.location.href = '/login.html'; return null; }
|
||||
@@ -396,6 +434,7 @@
|
||||
})
|
||||
.then(user => {
|
||||
if (user) {
|
||||
myUserId = user.userId;
|
||||
document.getElementById('greeting').textContent = 'Willkommen zurück, ' + user.name + '!';
|
||||
loadActiveGames(user.userId);
|
||||
loadActiveLock();
|
||||
@@ -798,10 +837,68 @@
|
||||
|
||||
const homePostCache = {};
|
||||
|
||||
let activeLbPostId = null;
|
||||
let activeLbPostType = null;
|
||||
|
||||
function homeOpenPost(postId) {
|
||||
const p = homePostCache[postId];
|
||||
if (p) sessionStorage.setItem('feedOpenPost', JSON.stringify(p));
|
||||
window.location.href = '/community/feed.html';
|
||||
if (!p) return;
|
||||
activeLbPostId = p.postId;
|
||||
activeLbPostType = p.postType || 'FEED';
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = renderHomePostCard(p);
|
||||
const card = tempDiv.firstElementChild;
|
||||
if (card) {
|
||||
card.querySelectorAll('.post-actions').forEach(el => el.remove());
|
||||
document.getElementById('lbPostBody').innerHTML = card.innerHTML;
|
||||
}
|
||||
loadLbComments(p.postId, activeLbPostType);
|
||||
document.getElementById('postLightbox').classList.add('open');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeLb() {
|
||||
document.getElementById('postLightbox').classList.remove('open');
|
||||
document.body.style.overflow = '';
|
||||
activeLbPostId = null;
|
||||
activeLbPostType = null;
|
||||
}
|
||||
|
||||
document.getElementById('postLightbox').addEventListener('click', e => {
|
||||
if (e.target === document.getElementById('postLightbox')) closeLb();
|
||||
});
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape' && document.getElementById('postLightbox').classList.contains('open')) closeLb();
|
||||
});
|
||||
|
||||
async function loadLbComments(postId, postType) {
|
||||
const targetType = postType === 'GROUP' ? 'GROUP_POST' : 'FEED_POST';
|
||||
try {
|
||||
const res = await fetch(`/social/kommentare?targetType=${targetType}&targetId=${postId}`);
|
||||
const comments = await res.json();
|
||||
document.getElementById('lbCommentsList').innerHTML = comments.length === 0
|
||||
? '<p style="color:var(--color-muted);font-size:0.82rem;margin:0.4rem;">Noch keine Kommentare.</p>'
|
||||
: comments.map(k => renderKommentarHtml(k, targetType, postId, { myUserId })).join('');
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
async function postLbComment() {
|
||||
if (!activeLbPostId) return;
|
||||
const input = document.getElementById('lbCommentInput');
|
||||
const text = input.value.trim();
|
||||
if (!text) return;
|
||||
const targetType = activeLbPostType === 'GROUP' ? 'GROUP_POST' : 'FEED_POST';
|
||||
await fetch('/social/kommentare', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ targetType, targetId: activeLbPostId, text })
|
||||
});
|
||||
input.value = '';
|
||||
await loadLbComments(activeLbPostId, activeLbPostType);
|
||||
}
|
||||
|
||||
async function deleteKommentar(kommentarId, targetType, targetId) {
|
||||
await fetch('/social/kommentare/' + kommentarId, { method: 'DELETE' });
|
||||
await loadLbComments(targetId, activeLbPostType);
|
||||
}
|
||||
|
||||
function renderHomePostCard(p) {
|
||||
|
||||
Reference in New Issue
Block a user