Weiter am Chastity Game gearbeitet und Interaktionen zwischen Keyholder und Lockee hinzugefügt

This commit is contained in:
2026-03-16 23:16:45 +01:00
parent 57a7c78037
commit 97c6f0a131
399 changed files with 34194 additions and 2272 deletions

View File

@@ -0,0 +1,69 @@
/**
* Gemeinsame Kartenanzeige für Chastity Game.
* Exportiert: CARD_LABELS, cardTypeGridHtml(cardCounts)
*/
(function () {
const style = document.createElement('style');
style.textContent = `
.card-type-grid {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin-top: 0.4rem;
}
.card-type-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
width: calc((100% - 6 * 0.6rem) / 14);
min-width: 28px;
}
.card-type-item img {
width: 100%;
height: auto;
border-radius: 4px;
display: block;
}
.card-type-badge {
font-size: 1rem;
font-weight: 700;
color: var(--color-text);
line-height: 1.2;
}
`;
document.head.appendChild(style);
})();
const CARD_LABELS = {
RED: { name: 'Rote Karte', img: '/img/card_red.png', desc: 'Verlängert die Sperrzeit. Je nach Konfiguration werden Minuten oder Stunden auf den Timer addiert.' },
GREEN: { name: 'Grüne Karte', img: '/img/card_green.png', desc: 'Verkürzt die Sperrzeit. Eine grüne Karte bringt dich dem Öffnen näher.' },
YELLOW: { name: 'Gelbe Karte', img: '/img/card_yellow.png', desc: 'Neutrales Ereignis keine Zeitveränderung, aber es passiert trotzdem etwas.' },
TASK: { name: 'Aufgabe', img: '/img/card_task.png', desc: 'Teilt dir eine zufällige Aufgabe aus der Aufgabenliste zu. Die Aufgabe muss erfüllt werden.' },
FREEZE: { name: 'Freeze', img: '/img/card_freeze.png', desc: 'Friert das Lock für eine festgelegte Zeit ein in diesem Zeitraum können keine Karten gezogen werden.' },
RESET: { name: 'Reset', img: '/img/card_reset.png', desc: 'Setzt das Kartendeck auf den Ausgangszustand zurück. Alle bisher gezogenen Karten kommen wieder rein.' },
DOUBLE_UP: { name: 'Double Up', img: '/img/card_doubleup.png', desc: 'Verdoppelt alle Karten im aktuellen Deck.' },
};
/**
* Gibt HTML für ein Karten-Typ-Raster zurück (ein Bild pro Typ, Anzahl-Badge).
* @param {Object} cardCounts { RED: 3, GREEN: 1, … }
* @returns {string} HTML-String
*/
function cardTypeGridHtml(cardCounts) {
if (!cardCounts || Object.keys(cardCounts).length === 0) {
return '<span style="color:var(--color-muted);font-size:0.85rem;">Keine Karten mehr im Stapel.</span>';
}
const items = Object.entries(cardCounts)
.filter(([, n]) => n > 0)
.map(([type, n]) => {
const info = CARD_LABELS[type] || { img: '/img/card.png', name: type };
return `<div class="card-type-item">
<img src="${info.img}" alt="${info.name}">
<span class="card-type-badge">${n}</span>
</div>`;
}).join('');
return items
? `<div class="card-type-grid">${items}</div>`
: '<span style="color:var(--color-muted);font-size:0.85rem;">Keine Karten mehr im Stapel.</span>';
}

View File

@@ -27,8 +27,11 @@
icon: '⊗',
items: [
{ href: '/infochastity.html', icon: '', label: 'Info' },
{ href: '/sessionchastity.html', icon: '▷', label: 'Neue Session', id: 'navChastityNeu' },
{ href: '#', icon: '▶', label: 'Aktive Session', id: 'navChastityAktiv' },
{ href: '/sessionchastity.html', icon: '▷', label: 'Neues Lock', id: 'navChastityNeu' },
{ href: '#', icon: '▶', label: 'Aktives Lock', id: 'navChastityAktiv' },
{ href: '/communityvotes.html', icon: '🗳️', label: 'Community Votes' },
{ href: '/meine-locks.html', icon: '🔒', label: 'Meine Locks' },
{ href: '/keyholder.html', icon: '🔑', label: 'Keyholder' },
]
},
];
@@ -96,7 +99,6 @@
// "Im Spiel" standardmäßig ausblenden; wird nach Session-Check ggf. wieder eingeblendet
const navNeu = document.getElementById('navBdsmNeu');
const navImSpiel = document.getElementById('navBdsmImSpiel');
const navCNeu = document.getElementById('navChastityNeu');
const navCAktiv = document.getElementById('navChastityAktiv');
if (navImSpiel) navImSpiel.style.display = 'none';
if (navCAktiv) navCAktiv.style.display = 'none';
@@ -121,7 +123,6 @@
if (lockRes.ok) {
const lockData = await lockRes.json();
const lockId = lockData.lockId;
if (navCNeu) navCNeu.style.display = 'none';
if (navCAktiv) {
navCAktiv.style.display = '';
navCAktiv.querySelector('a').href = '/sessionchastityingame.html?lockId=' + lockId;

View File

@@ -9,7 +9,8 @@
{ href: '/personen-suchen.html', icon: '⊕', label: 'Personen suchen', badgeId: null, mobileBadgeId: null },
{ href: '/freunde.html', icon: '♡', label: 'Freunde', badgeId: 'socialFriendsBadge', mobileBadgeId: 'socialMobileFriendsBadge' },
{ href: '/nachrichten.html', icon: '✉', label: 'Nachrichten', badgeId: 'socialMsgBadge', mobileBadgeId: 'socialMobileMsgBadge' },
{ href: '/gruppen.html', icon: '👥', label: 'Gruppen', badgeId: 'socialGruppenBadge', mobileBadgeId: 'socialMobileGruppenBadge' },
{ href: '/gruppen.html', icon: '👥', label: 'Gruppen', badgeId: 'socialGruppenBadge', mobileBadgeId: 'socialMobileGruppenBadge' },
{ href: '/einladungen.html', icon: '✉', label: 'Einladungen', badgeId: 'socialInvBadge', mobileBadgeId: 'socialMobileInvBadge' },
];
const profileActive = (path === '/benutzer.html' || path === '/profile.html') ? ' class="active"' : '';
@@ -116,4 +117,11 @@
fetch('/gruppen/reports/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0)
]).then(([joins, reports]) => setBadge(['socialGruppenBadge', 'socialMobileGruppenBadge'], joins + reports))
.catch(() => {});
Promise.all([
fetch('/keyholder/invitations/mine').then(r => r.ok ? r.json() : []).catch(() => []),
fetch('/lockee/invitations/mine').then(r => r.ok ? r.json() : []).catch(() => [])
]).then(([khInvs, lockeeInvs]) =>
setBadge(['socialInvBadge', 'socialMobileInvBadge'], khInvs.length + lockeeInvs.length)
).catch(() => {});
})();