Bugfixes im Vanilla game und refactoring
This commit is contained in:
@@ -254,6 +254,40 @@ public class AdminController {
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
// ── Item verschieben ─────────────────────────────────────────────────────
|
||||
|
||||
@PutMapping("/aufgabengruppen/items/{kind}/{itemId}/move")
|
||||
public ResponseEntity<Void> moveItem(
|
||||
@PathVariable("kind") String kind,
|
||||
@PathVariable("itemId") UUID itemId,
|
||||
@RequestParam("targetGruppeId") UUID targetGruppeId,
|
||||
Principal principal) {
|
||||
requireAdmin(principal);
|
||||
AufgabenGruppeEntity targetGruppe = aufgabenGruppeRepository.findById(targetGruppeId)
|
||||
.orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
|
||||
org.springframework.http.HttpStatus.NOT_FOUND, "Zielgruppe nicht gefunden"));
|
||||
switch (kind) {
|
||||
case "aufgabe" -> aufgabeRepository.findById(itemId).ifPresent(e -> {
|
||||
e.setAufgabenGruppe(targetGruppe);
|
||||
aufgabeRepository.save(e);
|
||||
});
|
||||
case "strafe" -> strafeRepository.findById(itemId).ifPresent(e -> {
|
||||
e.setAufgabenGruppe(targetGruppe);
|
||||
strafeRepository.save(e);
|
||||
});
|
||||
case "zeitstrafe" -> sperreRepository.findById(itemId).ifPresent(e -> {
|
||||
e.setAufgabenGruppe(targetGruppe);
|
||||
sperreRepository.save(e);
|
||||
});
|
||||
case "finisher" -> finisherRepository.findById(itemId).ifPresent(e -> {
|
||||
e.setAufgabenGruppe(targetGruppe);
|
||||
finisherRepository.save(e);
|
||||
});
|
||||
default -> { return ResponseEntity.badRequest().build(); }
|
||||
}
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
// ── Toys ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@GetMapping("/toys")
|
||||
|
||||
@@ -49,12 +49,10 @@ public class SecurityConfig {
|
||||
.requestMatchers("/games/chastity/sessionchastity.html").authenticated()
|
||||
.requestMatchers("/games/chastity/neulock.html").authenticated()
|
||||
.requestMatchers("/games/chastity/activelock.html").authenticated()
|
||||
.requestMatchers("/sessionbdsmtasks.html").authenticated()
|
||||
.requestMatchers("/sessionbdsmtoys.html").authenticated()
|
||||
.requestMatchers("/sessionbdsmingame.html").authenticated()
|
||||
.requestMatchers("/games/bdsm/neubdsm.html").authenticated()
|
||||
.requestMatchers("/games/bdsm/bdsmingame.html").authenticated()
|
||||
.requestMatchers("/games/bdsm/bdsmwarten.html").authenticated()
|
||||
.requestMatchers("/community/personen-suchen.html").authenticated()
|
||||
.requestMatchers("/community/freunde.html").authenticated()
|
||||
.requestMatchers("/community/nachrichten.html").authenticated()
|
||||
@@ -69,7 +67,7 @@ public class SecurityConfig {
|
||||
.requestMatchers("/games/chastity/meine-locks.html").authenticated()
|
||||
.requestMatchers("/games/chastity/entdecken-vorlagen.html").authenticated()
|
||||
.requestMatchers("/games/chastity/unlock-history.html").authenticated()
|
||||
.requestMatchers("/community/einladungen.html").authenticated()
|
||||
.requestMatchers("/games/common/einladungen.html").authenticated()
|
||||
.requestMatchers("/games/chastity/joinlock.html").authenticated()
|
||||
.requestMatchers("/community/benachrichtigungen.html").authenticated()
|
||||
.requestMatchers("/community/abonnements.html").authenticated()
|
||||
|
||||
@@ -23,7 +23,6 @@ import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity;
|
||||
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity.Status;
|
||||
import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository;
|
||||
import de.oaa.xxx.social.SystemMessageService;
|
||||
import de.oaa.xxx.social.entity.MessageCause;
|
||||
import de.oaa.xxx.social.repository.FriendshipRepository;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import de.oaa.xxx.user.UserService;
|
||||
@@ -97,13 +96,7 @@ public class BdsmEinladungController {
|
||||
entity.setCreatedAt(LocalDateTime.now());
|
||||
einladungRepository.save(entity);
|
||||
|
||||
String inviterName = userRepository.findById(inviterId).map(u -> u.getName()).orElse("Jemand");
|
||||
systemMessageService.send(
|
||||
inviterId, req.inviteeId(),
|
||||
inviterName + " hat dich zum BDSM Game eingeladen.",
|
||||
"/community/einladungen.html",
|
||||
MessageCause.INVITATION
|
||||
);
|
||||
systemMessageService.pushInvitationUpdate(req.inviteeId());
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("einladungId", entity.getEinladungId());
|
||||
@@ -118,10 +111,7 @@ public class BdsmEinladungController {
|
||||
if (e == null) return ResponseEntity.notFound().build();
|
||||
if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build();
|
||||
e.setStatus(Status.CANCELLED);
|
||||
String inviterName = userRepository.findById(userId).map(u -> u.getName()).orElse("Jemand");
|
||||
systemMessageService.send(userId, e.getInviteeId(),
|
||||
inviterName + " hat die BDSM-Spieleinladung zurückgezogen.",
|
||||
"/community/einladungen.html", MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(e.getInviteeId());
|
||||
return ResponseEntity.accepted().build();
|
||||
}
|
||||
|
||||
|
||||
@@ -189,10 +189,7 @@ public class CardLockController {
|
||||
inv.setDetailsVisible(req.lockeeDetailsVisible());
|
||||
lockeeInvitationRepository.save(inv);
|
||||
|
||||
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock";
|
||||
sendMessage(myId, lockee.getUserId(),
|
||||
me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.",
|
||||
"/community/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(lockee.getUserId());
|
||||
|
||||
return ResponseEntity.ok(Map.of("lockId", lock.getLockId().toString(), "lockeeInvitationSent", true));
|
||||
}
|
||||
@@ -261,10 +258,7 @@ public class CardLockController {
|
||||
inv.setCreatedAt(now);
|
||||
invitationRepository.save(inv);
|
||||
|
||||
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock";
|
||||
sendMessage(me.getUserId(), kh.getUserId(),
|
||||
me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.",
|
||||
"/community/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(kh.getUserId());
|
||||
|
||||
keyholderPending = true;
|
||||
}
|
||||
@@ -772,10 +766,7 @@ public class CardLockController {
|
||||
|
||||
if (lockOpt.isPresent()) {
|
||||
var lock = lockOpt.get();
|
||||
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
||||
sendMessage(myId, lock.getLockee(),
|
||||
me.getName() + " hat die Einladung als Keyholder*In für das Lock „" + lockName + "\" abgelehnt.",
|
||||
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(lock.getLockee());
|
||||
}
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
@@ -828,12 +819,7 @@ public class CardLockController {
|
||||
|
||||
invitationRepository.delete(inv);
|
||||
|
||||
String lockName = lockOpt.get().getName() != null && !lockOpt.get().getName().isBlank()
|
||||
? lockOpt.get().getName()
|
||||
: "Unbenanntes Lock";
|
||||
sendMessage(myId, inv.getKeyholderUserId(),
|
||||
me.getName() + " hat die Keyholder-Einladung für das Lock „" + lockName + "\" zurückgezogen.", null,
|
||||
de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(inv.getKeyholderUserId());
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import de.oaa.xxx.games.chastity.timelock.TimeLockRepository;
|
||||
import de.oaa.xxx.games.chastity.timelock.TimeLockServiceFactory;
|
||||
import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateEntity;
|
||||
import de.oaa.xxx.social.SystemMessageService;
|
||||
import de.oaa.xxx.social.entity.MessageCause;
|
||||
import de.oaa.xxx.subscription.SubscriptionLimitService;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import de.oaa.xxx.user.UserService;
|
||||
@@ -319,7 +318,6 @@ public class KeyholderOfferController {
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
|
||||
String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock";
|
||||
boolean invitationSent = false;
|
||||
if (!directStart) {
|
||||
// Normaler Einladungsworkflow: Keyholder muss bestätigen
|
||||
@@ -331,17 +329,11 @@ public class KeyholderOfferController {
|
||||
inv.setCreatedAt(LocalDateTime.now());
|
||||
invitationRepository.save(inv);
|
||||
|
||||
systemMessageService.send(myId, offerer.getUserId(),
|
||||
me.getName() + " möchte dein Keyholder-Angebot annehmen und lädt dich als Keyholder für „"
|
||||
+ lockName + "\" ein.",
|
||||
"/community/einladungen.html", MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(offerer.getUserId());
|
||||
invitationSent = true;
|
||||
} else {
|
||||
// Direktstart: Keyholder wird direkt gesetzt, aber trotzdem benachrichtigen
|
||||
systemMessageService.send(myId, offerer.getUserId(),
|
||||
me.getName() + " hat dein Keyholder-Angebot angenommen und das Lock „"
|
||||
+ lockName + "\" gestartet.",
|
||||
"/games/chastity/keyholder.html", MessageCause.INVITATION);
|
||||
// Direktstart: Keyholder wird direkt gesetzt
|
||||
systemMessageService.pushInvitationUpdate(offerer.getUserId());
|
||||
}
|
||||
|
||||
// Annahmezähler erhöhen
|
||||
|
||||
@@ -65,10 +65,6 @@ public class LockeeInvitationController {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl, de.oaa.xxx.social.entity.MessageCause cause) {
|
||||
systemMessageService.send(senderId, receiverId, text, targetUrl, cause);
|
||||
}
|
||||
|
||||
private String generateUnlockCode(int lines) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < lines; i++) sb.append(RNG.nextInt(10));
|
||||
@@ -165,10 +161,7 @@ public class LockeeInvitationController {
|
||||
if (lockOpt.isPresent()) {
|
||||
var lock = lockOpt.get();
|
||||
baseLockRepository.delete(lock);
|
||||
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
||||
sendMessage(myId, inv.getLockeeUserId(),
|
||||
me.getName() + " hat die Lockee-Einladung für das Lock „" + lockName + "\" zurückgezogen.",
|
||||
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(inv.getLockeeUserId());
|
||||
}
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
@@ -245,7 +238,6 @@ public class LockeeInvitationController {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
String unlockCode;
|
||||
String lockName;
|
||||
|
||||
if (lock instanceof CardLockEntity cardLock) {
|
||||
unlockCode = generateUnlockCode(codeLines);
|
||||
@@ -259,7 +251,6 @@ public class LockeeInvitationController {
|
||||
cardLock.setLastHygineOpening(now);
|
||||
}
|
||||
cardlockRepository.save(cardLock);
|
||||
lockName = cardLock.getName() != null && !cardLock.getName().isBlank() ? cardLock.getName() : "Unbenanntes Lock";
|
||||
} else if (lock instanceof TimeLockEntity timeLock) {
|
||||
unlockCode = CodeCreator.createNumeric(codeLines);
|
||||
int unlockMinutes = randomBetween(timeLock.getMinTimeInMinutes(), timeLock.getMaxTimeInMinutes());
|
||||
@@ -271,16 +262,13 @@ public class LockeeInvitationController {
|
||||
timeLock.setLastHygineOpening(now);
|
||||
}
|
||||
timeLockRepository.save(timeLock);
|
||||
lockName = timeLock.getName() != null && !timeLock.getName().isBlank() ? timeLock.getName() : "Unbenanntes Lock";
|
||||
} else {
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
|
||||
lockeeInvitationRepository.delete(inv);
|
||||
|
||||
sendMessage(myId, inv.getKeyholderUserId(),
|
||||
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" angenommen.",
|
||||
"/games/chastity/keyholder.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(inv.getKeyholderUserId());
|
||||
|
||||
return ResponseEntity.ok(Map.of(
|
||||
"lockId", lock.getLockId().toString(),
|
||||
@@ -305,10 +293,7 @@ public class LockeeInvitationController {
|
||||
if (lockOpt.isPresent()) {
|
||||
var lock = lockOpt.get();
|
||||
baseLockRepository.delete(lock);
|
||||
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
||||
sendMessage(myId, inv.getKeyholderUserId(),
|
||||
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" abgelehnt.",
|
||||
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(inv.getKeyholderUserId());
|
||||
}
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
|
||||
@@ -140,10 +140,7 @@ public class TimeLockController {
|
||||
inv.setDetailsVisible(req.lockeeDetailsVisible());
|
||||
lockeeInvitationRepository.save(inv);
|
||||
|
||||
String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock";
|
||||
systemMessageService.send(myId, lockee.getUserId(),
|
||||
me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.",
|
||||
"/community/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(lockee.getUserId());
|
||||
|
||||
return ResponseEntity.ok(Map.of(
|
||||
"lockId", lock.getLockId().toString(),
|
||||
@@ -178,10 +175,7 @@ public class TimeLockController {
|
||||
inv.setCreatedAt(LocalDateTime.now());
|
||||
invitationRepository.save(inv);
|
||||
|
||||
String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock";
|
||||
systemMessageService.send(myId, kh.getUserId(),
|
||||
me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.",
|
||||
"/community/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(kh.getUserId());
|
||||
|
||||
keyholderPending = true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.oaa.xxx.games.common.repository;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
@@ -37,4 +38,10 @@ public interface AufgabenGruppeRepository extends JpaRepository<AufgabenGruppeEn
|
||||
|
||||
@Query("SELECT g FROM AufgabenGruppeEntity g WHERE g.privateGruppe = false AND g.userId IS NOT NULL AND g.userId <> :userId AND g.strafen IS EMPTY AND g.sperren IS EMPTY AND (:name IS NULL OR LOWER(g.name) LIKE LOWER(:name))")
|
||||
List<AufgabenGruppeEntity> findVanillaSafePublicFromOthers(@Param("userId") UUID userId, @Param("name") String name);
|
||||
|
||||
@Query("SELECT g FROM AufgabenGruppeEntity g WHERE g.userId = :userId AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)")
|
||||
Page<AufgabenGruppeEntity> findByUserIdWithContent(@Param("userId") UUID userId, Pageable pageable);
|
||||
|
||||
@Query("SELECT g FROM AufgabenGruppeEntity g WHERE g.userId IS NULL AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)")
|
||||
Page<AufgabenGruppeEntity> findSystemGroupsWithContent(Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package de.oaa.xxx.games.common.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
|
||||
import de.oaa.xxx.games.common.entity.GruppenAboEntity;
|
||||
@@ -19,4 +23,8 @@ public interface GruppenAboRepository extends JpaRepository<GruppenAboEntity, UU
|
||||
long countByAufgabenGruppe(AufgabenGruppeEntity gruppe);
|
||||
|
||||
void deleteByAufgabenGruppe(AufgabenGruppeEntity gruppe);
|
||||
|
||||
@Query(value = "SELECT a FROM GruppenAboEntity a JOIN a.aufgabenGruppe g WHERE a.userId = :userId AND g.privateGruppe = false AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)",
|
||||
countQuery = "SELECT COUNT(a) FROM GruppenAboEntity a JOIN a.aufgabenGruppe g WHERE a.userId = :userId AND g.privateGruppe = false AND (g.aufgaben IS NOT EMPTY OR g.finisher IS NOT EMPTY)")
|
||||
Page<GruppenAboEntity> findByUserIdWithContent(@Param("userId") UUID userId, Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package de.oaa.xxx.games.vanilla.controller;
|
||||
|
||||
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppe;
|
||||
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppePage;
|
||||
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
|
||||
import de.oaa.xxx.games.common.entity.GruppenAboEntity;
|
||||
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
|
||||
import de.oaa.xxx.games.common.repository.GruppenAboRepository;
|
||||
import de.oaa.xxx.user.UserEntity;
|
||||
import de.oaa.xxx.user.UserService;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@@ -18,13 +19,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppe;
|
||||
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppePage;
|
||||
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
|
||||
import de.oaa.xxx.games.common.entity.GruppenAboEntity;
|
||||
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
|
||||
import de.oaa.xxx.games.common.repository.GruppenAboRepository;
|
||||
import de.oaa.xxx.user.UserEntity;
|
||||
import de.oaa.xxx.user.UserService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/vanilla/abo")
|
||||
@@ -56,15 +58,17 @@ public class VanillaAboController {
|
||||
Principal principal) {
|
||||
UserEntity user = userService.requireUser(principal);
|
||||
|
||||
List<AufgabenGruppe> dtos = aboRepository.findByUserId(user.getUserId()).stream()
|
||||
.map(GruppenAboEntity::getAufgabenGruppe)
|
||||
.filter(g -> !g.isPrivateGruppe())
|
||||
.filter(g -> g.getStrafen().isEmpty() && g.getSperren().isEmpty())
|
||||
.map(g -> enrich(g, user.getUserId(), true))
|
||||
.sorted(Comparator.comparing(AufgabenGruppe::getName, String.CASE_INSENSITIVE_ORDER))
|
||||
Page<GruppenAboEntity> dbPage = aboRepository.findByUserIdWithContent(
|
||||
user.getUserId(), PageRequest.of(page, size, Sort.by("aufgabenGruppe.name")));
|
||||
List<AufgabenGruppe> dtos = dbPage.getContent().stream()
|
||||
.map(a -> enrich(a.getAufgabenGruppe(), user.getUserId(), true))
|
||||
.toList();
|
||||
|
||||
return ResponseEntity.ok(manualPage(dtos, page, size));
|
||||
AufgabenGruppePage result = new AufgabenGruppePage();
|
||||
result.setContent(dtos);
|
||||
result.setCurrentPage(dbPage.getNumber());
|
||||
result.setTotalPages(dbPage.getTotalPages());
|
||||
result.setTotalElements(dbPage.getTotalElements());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
// ── Entdecken (nur vanilla-safe Gruppen von anderen) ──
|
||||
@@ -82,11 +86,19 @@ public class VanillaAboController {
|
||||
List<AufgabenGruppe> dtos = gruppeRepository
|
||||
.findVanillaSafePublicFromOthers(user.getUserId(), namePattern).stream()
|
||||
.map(g -> enrich(g, user.getUserId(), aboRepository.existsByUserIdAndAufgabenGruppe(user.getUserId(), g)))
|
||||
.sorted(Comparator.comparingLong(AufgabenGruppe::getSubscriberCount).reversed()
|
||||
.sorted(java.util.Comparator.comparingLong(AufgabenGruppe::getSubscriberCount).reversed()
|
||||
.thenComparing(AufgabenGruppe::getName, String.CASE_INSENSITIVE_ORDER))
|
||||
.toList();
|
||||
|
||||
return ResponseEntity.ok(manualPage(dtos, page, size));
|
||||
int total = dtos.size();
|
||||
int start = page * size;
|
||||
List<AufgabenGruppe> content = start >= total ? List.of() : dtos.subList(start, Math.min(start + size, total));
|
||||
AufgabenGruppePage discoverPage = new AufgabenGruppePage();
|
||||
discoverPage.setContent(content);
|
||||
discoverPage.setCurrentPage(page);
|
||||
discoverPage.setTotalPages(total == 0 ? 1 : (int) Math.ceil((double) total / size));
|
||||
discoverPage.setTotalElements(total);
|
||||
return ResponseEntity.ok(discoverPage);
|
||||
}
|
||||
|
||||
// ── Abonnieren (nur vanilla-safe) ──
|
||||
@@ -138,16 +150,5 @@ public class VanillaAboController {
|
||||
return g;
|
||||
}
|
||||
|
||||
private AufgabenGruppePage manualPage(List<AufgabenGruppe> all, int page, int size) {
|
||||
int total = all.size();
|
||||
int start = page * size;
|
||||
List<AufgabenGruppe> content = start >= total ? List.of() : all.subList(start, Math.min(start + size, total));
|
||||
AufgabenGruppePage result = new AufgabenGruppePage();
|
||||
result.setContent(content);
|
||||
result.setCurrentPage(page);
|
||||
result.setTotalPages(total == 0 ? 1 : (int) Math.ceil((double) total / size));
|
||||
result.setTotalElements(total);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,18 +84,17 @@ public class VanillaAufgabenGruppeController {
|
||||
Principal principal) {
|
||||
UserEntity user = resolveUser(principal);
|
||||
if (user == null) return ResponseEntity.status(401).build();
|
||||
// Only vanilla-safe user groups (no Strafen, no Sperren)
|
||||
UUID userId = user.getUserId();
|
||||
String searchPattern = null;
|
||||
java.util.List<AufgabenGruppeEntity> all = gruppeRepository.listVanillaSafeWithUserAndSearch(
|
||||
userId, searchPattern, PageRequest.of(0, 500, Sort.by("name")));
|
||||
java.util.List<AufgabenGruppeEntity> ownOnly = all.stream()
|
||||
.filter(g -> userId.equals(g.getUserId())).toList();
|
||||
AufgabenGruppePage result = manualPage(ownOnly.stream().map(entity -> {
|
||||
Page<AufgabenGruppeEntity> dbPage = gruppeRepository.findByUserIdWithContent(
|
||||
user.getUserId(), PageRequest.of(page, size, Sort.by("name")));
|
||||
AufgabenGruppePage result = new AufgabenGruppePage();
|
||||
result.setContent(dbPage.getContent().stream().map(entity -> {
|
||||
AufgabenGruppe g = entity.toAufgabenGruppe();
|
||||
g.setSubscriberCount(aboRepository.countByAufgabenGruppe(entity));
|
||||
return g;
|
||||
}).toList(), page, DEFAULT_PAGE_SIZE);
|
||||
}).toList());
|
||||
result.setCurrentPage(dbPage.getNumber());
|
||||
result.setTotalPages(dbPage.getTotalPages());
|
||||
result.setTotalElements(dbPage.getTotalElements());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@@ -103,16 +102,14 @@ public class VanillaAufgabenGruppeController {
|
||||
public ResponseEntity<AufgabenGruppePage> listSystem(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size) {
|
||||
// Only vanilla-safe system groups (userId IS NULL → no strafen/sperren anyway, but filter for safety)
|
||||
Page<AufgabenGruppeEntity> result = gruppeRepository.findByUserIdIsNull(
|
||||
Page<AufgabenGruppeEntity> dbPage = gruppeRepository.findSystemGroupsWithContent(
|
||||
PageRequest.of(page, size, Sort.by("name")));
|
||||
AufgabenGruppePage r = new AufgabenGruppePage();
|
||||
r.setContent(result.getContent().stream()
|
||||
.filter(g -> g.getStrafen().isEmpty() && g.getSperren().isEmpty())
|
||||
r.setContent(dbPage.getContent().stream()
|
||||
.map(AufgabenGruppeEntity::toAufgabenGruppe).toList());
|
||||
r.setCurrentPage(result.getNumber());
|
||||
r.setTotalPages(result.getTotalPages());
|
||||
r.setTotalElements(result.getTotalElements());
|
||||
r.setCurrentPage(dbPage.getNumber());
|
||||
r.setTotalPages(dbPage.getTotalPages());
|
||||
r.setTotalElements(dbPage.getTotalElements());
|
||||
return ResponseEntity.ok(r);
|
||||
}
|
||||
|
||||
@@ -270,15 +267,4 @@ public class VanillaAufgabenGruppeController {
|
||||
return userService.requireUser(principal);
|
||||
}
|
||||
|
||||
private AufgabenGruppePage manualPage(java.util.List<AufgabenGruppe> all, int page, int size) {
|
||||
int total = all.size();
|
||||
int start = page * size;
|
||||
java.util.List<AufgabenGruppe> content = start >= total ? java.util.List.of() : all.subList(start, Math.min(start + size, total));
|
||||
AufgabenGruppePage result = new AufgabenGruppePage();
|
||||
result.setContent(content);
|
||||
result.setCurrentPage(page);
|
||||
result.setTotalPages(total == 0 ? 1 : (int) Math.ceil((double) total / size));
|
||||
result.setTotalElements(total);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import de.oaa.xxx.games.vanilla.entity.VanillaEinladungEntity;
|
||||
import de.oaa.xxx.games.vanilla.entity.VanillaEinladungEntity.Status;
|
||||
import de.oaa.xxx.games.vanilla.repository.VanillaEinladungRepository;
|
||||
import de.oaa.xxx.social.SystemMessageService;
|
||||
import de.oaa.xxx.social.entity.MessageCause;
|
||||
import de.oaa.xxx.social.repository.FriendshipRepository;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import de.oaa.xxx.user.UserService;
|
||||
@@ -106,13 +105,7 @@ public class VanillaEinladungController {
|
||||
entity.setCreatedAt(LocalDateTime.now());
|
||||
einladungRepository.save(entity);
|
||||
|
||||
String inviterName = userRepository.findById(inviterId).map(u -> u.getName()).orElse("Jemand");
|
||||
systemMessageService.send(
|
||||
inviterId, req.inviteeId(),
|
||||
inviterName + " hat dich zum Vanilla Game eingeladen.",
|
||||
"/community/einladungen.html",
|
||||
MessageCause.INVITATION
|
||||
);
|
||||
systemMessageService.pushInvitationUpdate(req.inviteeId());
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("einladungId", entity.getEinladungId());
|
||||
@@ -127,10 +120,7 @@ public class VanillaEinladungController {
|
||||
if (e == null) return ResponseEntity.notFound().build();
|
||||
if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build();
|
||||
e.setStatus(Status.CANCELLED);
|
||||
String inviterName = userRepository.findById(userId).map(u -> u.getName()).orElse("Jemand");
|
||||
systemMessageService.send(userId, e.getInviteeId(),
|
||||
inviterName + " hat die Vanilla-Spieleinladung zurückgezogen.",
|
||||
"/community/einladungen.html", MessageCause.INVITATION);
|
||||
systemMessageService.pushInvitationUpdate(e.getInviteeId());
|
||||
return ResponseEntity.accepted().build();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,8 @@ public class SystemMessageService {
|
||||
.findByUserIdAndCause(receiverId, cause)
|
||||
.orElseGet(() -> NotificationPreferenceEntity.defaultFor(receiverId, cause));
|
||||
|
||||
// FRIENDREQUEST ist immer in-app, unabhängig von der Einstellung
|
||||
boolean sendInApp = cause == MessageCause.FRIENDREQUEST || pref.isInApp();
|
||||
// FRIENDREQUEST und INVITATION sind immer nur in-app, kein E-Mail
|
||||
boolean sendInApp = cause == MessageCause.FRIENDREQUEST || cause == MessageCause.INVITATION || pref.isInApp();
|
||||
|
||||
if (sendInApp) {
|
||||
MessageEntity msg = new MessageEntity();
|
||||
@@ -76,7 +76,7 @@ public class SystemMessageService {
|
||||
sseService.push(receiverId, "NOTIFICATION", Map.of("unreadCount", unread, "text", text));
|
||||
}
|
||||
|
||||
if (pref.isEmail()) {
|
||||
if (pref.isEmail() && cause != MessageCause.INVITATION) {
|
||||
userRepository.findById(receiverId).ifPresent(user -> {
|
||||
try {
|
||||
Email email = new Email();
|
||||
@@ -91,6 +91,15 @@ public class SystemMessageService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Benachrichtigt den Empfänger per SSE, dass sich seine Einladungsliste geändert hat,
|
||||
* ohne eine In-App-Nachricht oder E-Mail zu erstellen.
|
||||
*/
|
||||
public void pushInvitationUpdate(UUID receiverId) {
|
||||
if (receiverId == null) return;
|
||||
sseService.push(receiverId, "INVITATION", java.util.Map.of());
|
||||
}
|
||||
|
||||
private String causeTitel(MessageCause cause) {
|
||||
return switch (cause) {
|
||||
case INVITATION -> "XXX The Game – Neue Einladung";
|
||||
|
||||
588
xxxthegame/src/main/resources/sql/testdata_aufgabengruppen.sql
Normal file
588
xxxthegame/src/main/resources/sql/testdata_aufgabengruppen.sql
Normal file
@@ -0,0 +1,588 @@
|
||||
-- ============================================================
|
||||
-- Testdaten: Aufgabengruppen (generiert aus DefaultFiller)
|
||||
-- Toys und *Toy-Join-Tabellen werden ignoriert.
|
||||
-- UUID-Speicherung: varchar(36) als plain UUID-String
|
||||
-- Spaltennamen: SpringPhysicalNamingStrategy → snake_case
|
||||
-- ============================================================
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ── Aufgabengruppen ──────────────────────────────────────────
|
||||
INSERT IGNORE INTO aufgaben_gruppe (gruppen_id, name, beschreibung, user_id, private_gruppe, bild, von) VALUES
|
||||
('10000000-0000-0000-0000-000000000001', 'Keuschhaltung weiblich', 'Enthält verschiedene Aufgaben für Keuschhaltung von weiblichen Spielpartnern', NULL, 0, NULL, NULL),
|
||||
('10000000-0000-0000-0000-000000000002', 'Keuschhaltung männlich', 'Enthält verschiedene Aufgaben für Keuschhaltung von männlichen Spielpartnern', NULL, 0, NULL, NULL),
|
||||
('10000000-0000-0000-0000-000000000003', 'Plugs', 'Enthält verschiedene Aufgaben für das Tragen von Buttplugs über einen gewissen Zeitraum.', NULL, 0, NULL, NULL),
|
||||
('10000000-0000-0000-0000-000000000004', 'Knebel', 'Enthält verschiedene Aufgaben für das Tragen von Knebeln über einen gewissen Zeitraum.', NULL, 0, NULL, NULL),
|
||||
('10000000-0000-0000-0000-000000000005', 'Strafen', 'Enthält verschiedene Bestrafungen', NULL, 0, NULL, NULL),
|
||||
('10000000-0000-0000-0000-000000000006', 'Aufgaben', 'Enthält verschiedene Sex-Aufgaben.', NULL, 0, NULL, NULL);
|
||||
|
||||
|
||||
-- ── Sperren ──────────────────────────────────────────────────
|
||||
-- Gruppe: Keuschhaltung weiblich
|
||||
INSERT IGNORE INTO sperre (sperre_id, kurz_text, text, release_text, minuten_von, minuten_bis, gruppe_id) VALUES
|
||||
('20000000-0000-0000-0000-000000000001', 'Voll-KG',
|
||||
'{PASSIV} trägt fortan einen Voll-KG, {AKTIV} ist der Keyholder',
|
||||
'{AKTIV}, es ist ab der Zeit {PASSIV} von ihrem KG zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000001'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000002', 'Voll-KG + Vaginaldildo',
|
||||
'{PASSIV} trägt fortan einen Voll-KG mit Vaginaldildo, {AKTIV} ist der Keyholder',
|
||||
'{AKTIV}, es ist ab der Zeit {PASSIV} von ihrem KG zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000001'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000003', 'Voll-KG + Analdildo',
|
||||
'{PASSIV} trägt fortan einen Voll-KG mit Analdildo, {AKTIV} ist der Keyholder',
|
||||
'{AKTIV}, es ist ab der Zeit {PASSIV} von ihrem KG zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000001'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000004', 'Voll-KG + Doubleplugged',
|
||||
'{PASSIV} trägt fortan einen Voll-KG mit Vaginal- und Analdildo, {AKTIV} ist der Keyholder',
|
||||
'{AKTIV}, es ist ab der Zeit {PASSIV} von ihrem KG zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000001');
|
||||
|
||||
-- Gruppe: Keuschhaltung männlich
|
||||
INSERT IGNORE INTO sperre (sperre_id, kurz_text, text, release_text, minuten_von, minuten_bis, gruppe_id) VALUES
|
||||
('20000000-0000-0000-0000-000000000005', 'Peniskäfig',
|
||||
'{PASSIV} trägt fortan einen Peniskäfig, {AKTIV} ist der Keyholder',
|
||||
'{AKTIV}, es ist ab der Zeit {PASSIV} von seinem Peniskäfig zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000002'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000006', 'Voll-KG',
|
||||
'{PASSIV} trägt fortan einen Voll-KG, {AKTIV} ist der Keyholder',
|
||||
'{AKTIV}, es ist ab der Zeit {PASSIV} von seinem KG zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000002'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000007', 'Voll-KG + Analdildo',
|
||||
'{PASSIV} trägt fortan einen Voll-KG mit Analdildo, {AKTIV} ist der Keyholder',
|
||||
'{AKTIV}, es ist ab der Zeit {PASSIV} von seinem KG zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000002');
|
||||
|
||||
-- Gruppe: Plugs
|
||||
INSERT IGNORE INTO sperre (sperre_id, kurz_text, text, release_text, minuten_von, minuten_bis, gruppe_id) VALUES
|
||||
('20000000-0000-0000-0000-000000000008', 'Plug klein',
|
||||
'{AKTIV} führt {PASSIV} einen kleinen Buttplug in anal ein, dieser ist bis auf weiteres zu tragen.',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Plug zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000003'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000009', 'Plug mittel',
|
||||
'{AKTIV} führt {PASSIV} einen mittelgroßen Buttplug anal ein, dieser ist bis auf weiteres zu tragen.',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Plug zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000003'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000010', 'Plug groß',
|
||||
'{AKTIV} führt {PASSIV} einen großen Buttplug anal ein, dieser ist bis auf weiteres zu tragen.',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Plug zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000003'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000011', 'Elektro-Plug anal',
|
||||
'{AKTIV} führt {PASSIV} einen Elekro-Plug anal ein, dieser ist bis auf weiteres zu tragen. {AKTIV} darf {PASSIV} leichte Stromstöße verpassen',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Plug zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000003'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000012', 'Elektro-Plug vaginal',
|
||||
'{AKTIV} führt {PASSIV} einen Elekto-Plug vaginal ein, dieser ist bis auf weiteres zu tragen. {AKTIV} darf {PASSIV} leichte Stromstöße verpassen',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Plug zu befreien',
|
||||
10, 30, '10000000-0000-0000-0000-000000000003');
|
||||
|
||||
-- Gruppe: Knebel
|
||||
INSERT IGNORE INTO sperre (sperre_id, kurz_text, text, release_text, minuten_von, minuten_bis, gruppe_id) VALUES
|
||||
('20000000-0000-0000-0000-000000000013', 'Ballknebel',
|
||||
'{AKTIV}, lege {PASSIV} einen Ballknebel an, dieser ist bis auf weiteres zu tragen.',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Knebel zu befreien.',
|
||||
10, 30, '10000000-0000-0000-0000-000000000004'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000014', 'Penisknebel',
|
||||
'{AKTIV}, lege {PASSIV} einen Dildoknebel an, dieser ist bis auf weiteres zu tragen.',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Knebel zu befreien.',
|
||||
10, 30, '10000000-0000-0000-0000-000000000004'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000015', 'Aufblasbarer Knebel',
|
||||
'{AKTIV}, lege {PASSIV} einen aufblasbaren Knebel an und pumpe diesen soweit auf, dass {PASSIV} noch halbwegs gut atmen kann, dieser ist bis auf weiteres zu tragen.',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Knebel zu befreien.',
|
||||
5, 15, '10000000-0000-0000-0000-000000000004'),
|
||||
|
||||
('20000000-0000-0000-0000-000000000016', 'Isolationsmaske',
|
||||
'{AKTIV}, lege {PASSIV} eine Isolationsmaske an, diese ist bis auf weiteres zu tragen.',
|
||||
'{AKTIV}, es ist Zeit {PASSIV} von seinem Knebel zu befreien.',
|
||||
5, 15, '10000000-0000-0000-0000-000000000004');
|
||||
|
||||
-- sperre_sperre_fuer (war @CollectionTable name="sperre_sperreFuer" → snake_case)
|
||||
INSERT IGNORE INTO sperre_sperre_fuer (sperre_id, werkzeug) VALUES
|
||||
('20000000-0000-0000-0000-000000000001', 'VAGINA'),
|
||||
('20000000-0000-0000-0000-000000000002', 'VAGINA'),
|
||||
('20000000-0000-0000-0000-000000000003', 'VAGINA'),
|
||||
('20000000-0000-0000-0000-000000000003', 'ANUS'),
|
||||
('20000000-0000-0000-0000-000000000004', 'VAGINA'),
|
||||
('20000000-0000-0000-0000-000000000004', 'ANUS'),
|
||||
('20000000-0000-0000-0000-000000000005', 'PENIS'),
|
||||
('20000000-0000-0000-0000-000000000006', 'PENIS'),
|
||||
('20000000-0000-0000-0000-000000000007', 'PENIS'),
|
||||
('20000000-0000-0000-0000-000000000007', 'ANUS'),
|
||||
('20000000-0000-0000-0000-000000000008', 'ANUS'),
|
||||
('20000000-0000-0000-0000-000000000009', 'ANUS'),
|
||||
('20000000-0000-0000-0000-000000000010', 'ANUS'),
|
||||
('20000000-0000-0000-0000-000000000011', 'ANUS'),
|
||||
('20000000-0000-0000-0000-000000000012', 'VAGINA'),
|
||||
('20000000-0000-0000-0000-000000000013', 'MUND'),
|
||||
('20000000-0000-0000-0000-000000000014', 'MUND'),
|
||||
('20000000-0000-0000-0000-000000000015', 'MUND'),
|
||||
('20000000-0000-0000-0000-000000000016', 'MUND');
|
||||
|
||||
|
||||
-- ── Strafen ──────────────────────────────────────────────────
|
||||
INSERT IGNORE INTO strafe (strafe_id, kurz_text, text, level, sekunden_von, sekunden_bis, gruppe_id) VALUES
|
||||
('30000000-0000-0000-0000-000000000001', '5 Schläge mit flachen Hand',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 5 Schläge mit der flachen Hand auf das Gesäß.',
|
||||
1, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000002', '15 Schläge mit flachen Hand',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 15 beherzte Schläge mit der flachen Hand auf das Gesäß, {PASSIV} zählt laut mit',
|
||||
3, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000003', '5 Schläge mit Gerte',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 5 Schläge mit der Gerte auf das Gesäß.',
|
||||
2, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000004', '15 Schläge mit Gerte',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 15 beherzte Schläge mit der Gerte auf das Gesäß, {PASSIV} zählt laut mit',
|
||||
4, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000005', '5 Schläge mit Paddel',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 5 Schläge mit dem Paddel auf das Gesäß.',
|
||||
2, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000006', '15 Schläge mit Paddel',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 15 beherzte Schläge mit dem Paddel auf das Gesäß, {PASSIV} zählt laut mit',
|
||||
4, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000007', '5 Schläge mit Peitsche',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 5 Schläge mit der Peitsche auf das Gesäß.',
|
||||
3, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000008', '15 Schläge mit Peitsche',
|
||||
'{PASSIV} stellt sich mit dem Gesicht zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 15 beherzte Schläge mit der Peitsche auf das Gesäß, {PASSIV} zählt laut mit',
|
||||
5, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000009', 'Schläge auf Klitoris mit Hand',
|
||||
'{PASSIV} liegt auf dem Rücken mit breiten Beinen, {AKTIV} verpasst {PASSIV} 5 Schläge mit der Hand auf die Klitoris, {PASSIV} zählt laut mit',
|
||||
4, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000010', 'Schläge auf Klitoris mit Peitsche',
|
||||
'{PASSIV} liegt auf dem Rücken mit breiten Beinen, {AKTIV} verpasst {PASSIV} 5 Schläge mit der Peitsche auf die Klitoris, {PASSIV} zählt laut mit',
|
||||
5, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000011', 'Schläge auf Klitoris mit Paddel',
|
||||
'{PASSIV} liegt auf dem Rücken mit breiten Beinen, {AKTIV} verpasst {PASSIV} 5 Schläge mit dem Paddel auf die Klitoris, {PASSIV} zählt laut mit',
|
||||
5, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000012', 'Schläge auf Klitoris mit Gerte',
|
||||
'{PASSIV} liegt auf dem Rücken mit breiten Beinen, {AKTIV} verpasst {PASSIV} 5 Schläge mit der Gerte auf die Klitoris, {PASSIV} zählt laut mit',
|
||||
5, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000013', '5 Ohrfeigen',
|
||||
'{PASSIV} stellt sich mit dem Rücken zur Wand, Hände hinterm Kopf, Beine schulterbreit, {AKTIV} verpasst {PASSIV} 5 Ohrfeigen, {PASSIV} zählt laut mit',
|
||||
5, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000014', 'Elektroplug anal',
|
||||
'{AKTIV} führt {PASSIV} anal einen Elektro-Plug ein. {AKTIV} erhöht ganz langsam die Intensität bis {PASSIV} ''STOP'' sagt, dann fängt {AKTIV} wieder bei null an',
|
||||
5, 30, 90, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000015', 'Elektroplug vaginal',
|
||||
'{AKTIV} führt {PASSIV} vaginal einen Elektro-Plug ein. {AKTIV} erhöht ganz langsam die Intensität bis {PASSIV} ''STOP'' sagt, dann fängt {AKTIV} wieder bei null an',
|
||||
5, 30, 90, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000016', 'Pumpplug anal',
|
||||
'{AKTIV} führt {PASSIV} anal einen Pump-Plug ein. {AKTIV} pumpt ganz langsam auf bis {PASSIV} ''STOP'' sagt, dann fängt {AKTIV} wieder bei null an',
|
||||
5, 30, 90, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000017', 'Pumpplug vaginal',
|
||||
'{AKTIV} führt {PASSIV} vaginal einen Pump-Plug ein. {AKTIV} pumpt ganz langsam auf bis {PASSIV} ''STOP'' sagt, dann fängt {AKTIV} wieder bei null an',
|
||||
5, 30, 90, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000018', 'Facesitting (Vagina)',
|
||||
'{PASSIV} liegt auf dem Rücken, {AKTIV} setzt sich auf das Gesicht von {PASSIV} und lässt sich den Vaginal und/oder Analbereich verwöhnen',
|
||||
2, 90, 180, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000019', 'Facesitting gefesselt (Vagina)',
|
||||
'{PASSIV} liegt mit auf den Rücken gefesselten Händen auf dem Rücken, {AKTIV} setzt sich auf das Gesicht von {PASSIV} und lässt sich den Vaginal und/oder Analbereich verwöhnen',
|
||||
4, 90, 180, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000020', 'Facesitting (Penis)',
|
||||
'{PASSIV} liegt auf dem Rücken, {AKTIV} setzt sich auf das Gesicht von {PASSIV} und lässt sich den Penis und/oder Analbereich verwöhnen',
|
||||
2, 90, 180, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000021', 'Facesitting gefesselt (Penis)',
|
||||
'{PASSIV} liegt mit auf den Rücken gefesselten Händen auf dem Rücken, {AKTIV} setzt sich auf das Gesicht von {PASSIV} und lässt sich den Penis und/oder Analbereich verwöhnen',
|
||||
4, 90, 180, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000022', 'Facesitting Doppelpenisknebel',
|
||||
'{PASSIV} liegt auf dem Rücken, {AKTIV} legt {PASSIV} einen Doppel-Penisknebel an und reitet diesen vaginal oder anal',
|
||||
3, 60, 120, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000023', 'Facesitting Doppelpenisknebel gefesselt',
|
||||
'{PASSIV} liegt mit auf den Rücken gefesselten Händen auf dem Rücken, {AKTIV} legt {PASSIV} einen Doppel-Penisknebel an und reitet diesen vaginal oder anal',
|
||||
3, 60, 120, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000024', 'Nippelklemmen',
|
||||
'{AKTIV} legt {PASSIV} Nippelklemmen an, {AKTIV} zieht an der Kette und erhöht ganz langsam die Intensität bis {PASSIV} ''STOP'' sagt, dann fängt {AKTIV} wieder bei null an',
|
||||
3, 30, 90, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000025', 'Nippelbehandlung',
|
||||
'{AKTIV} nimmt die Nippel von {PASSIV} zwischen die Finger und erhöht langsam den Druck bis {PASSIV} ''STOP'' sagt',
|
||||
2, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000026', 'Hilflos liegen lassen',
|
||||
'{AKTIV} fesselt, knebelt und verbindet die Augen von {PASSIV}. {AKTIV} lässt {PASSIV} wehrlos liegen, bei Ablauf der Zeit erlöst {AKTIV} {PASSIV} mit einem beherzten Platsch auf den Po',
|
||||
4, 300, 600, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000027', 'Strapon reiten',
|
||||
'{PASSIV} liegt auf dem Rücken und trägt dabei einen Umschnalldildo. {AKTIV} reitet den Umschnalldildo von {PASSIV}',
|
||||
3, 60, 180, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000028', 'Strapon reiten gefesselt',
|
||||
'{AKTIV} fesselt und knebelt {PASSIV}. {PASSIV} trägt dabei einen Umschnalldildo. {AKTIV} reitet den Umschnalldildo von {PASSIV}',
|
||||
4, 60, 180, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000029', 'Teaseblowjob mit dem Strapon',
|
||||
'{AKTIV} fesselt und knebelt {PASSIV}. {PASSIV} trägt dabei einen Umschnalldildo, KG und einen großen Buttplug. {AKTIV} gibt dem Umschnalldildo einen Blowjob in 69er Position und präsentiert {PASSIV} dabei den Intimbereich',
|
||||
5, 180, 300, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000030', 'Teasereiten mit Strapon',
|
||||
'{AKTIV} fesselt und knebelt {PASSIV}. {PASSIV} trägt dabei einen Umschnalldildo, KG und einen großen Buttplug. {AKTIV} reitet den Umschnalldildo von {PASSIV}.',
|
||||
5, 180, 300, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000031', 'Tease mit Selbstbefriedigung (Mann KG)',
|
||||
'{AKTIV} knebelt und fesselt {PASSIV} an einen Stuhl. {PASSIV} trägt dabei einen KG und einen großen Buttplug. {AKTIV} befriedigt sich dann vor den Augen von {PASSIV} selber',
|
||||
4, 240, 360, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000032', 'Tease mit Selbstbefriedigung (Frau KG)',
|
||||
'{AKTIV} knebelt und fesselt {PASSIV} an einen Stuhl. {PASSIV} trägt dabei einen KG und einen großen Buttplug. {AKTIV} befriedigt sich dann vor den Augen von {PASSIV} selber',
|
||||
4, 240, 360, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000033', 'Blowjob auf allen vieren',
|
||||
'{AKTIV}, zwinge {PASSIV} vor dir auf die Knie, führe dein Glied (oder Strap on) in den Mund von {PASSIV} ein und zeig mit einem Deepthroat, wer das sagen hat',
|
||||
5, 30, 90, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000034', 'Oralsex mit kleinem Dildo in der Vagina',
|
||||
'{PASSIV}, geh auf die Knie und reite vaginal einen kleinen Dildo, befriedige dabei {AKTIV} oral.',
|
||||
2, 60, 120, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000035', 'Oralsex mit großen Dildo in der Vagina',
|
||||
'{PASSIV}, geh auf die Knie und reite vaginal einen großen Dildo, befriedige dabei {AKTIV} oral.',
|
||||
4, 60, 120, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000036', 'Oralsex mit kleinem Dildo im Anus',
|
||||
'{PASSIV}, geh auf die Knie und reite anal einen kleinen Dildo, befriedige dabei {AKTIV} oral.',
|
||||
3, 60, 120, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000037', 'Oralsex mit großen Dildo im Anus',
|
||||
'{PASSIV}, geh auf die Knie und reite anal einen großen Dildo, befriedige dabei {AKTIV} oral.',
|
||||
4, 60, 120, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000038', 'Vagina dehnen',
|
||||
'{PASSIV} geht auf alle viere und streckt den Hintern schön in die Luft, {AKTIV} führe langsam nach und nach mehr Finger in die Vagina von {PASSIV} ein, bis {PASSIV} ''STOP'' sagt',
|
||||
2, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000039', 'Anus dehnen',
|
||||
'{PASSIV} geht auf alle viere und streckt den Hintern schön in die Luft, {AKTIV} führe langsam nach und nach mehr Finger in die Anus von {PASSIV} ein, bis {PASSIV} ''STOP'' sagt',
|
||||
2, NULL, NULL, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000040', 'Vaginalsex in Missionarstellung und Breathplay',
|
||||
'{AKTIV} dringt in Missionarsstellung in {PASSIV} und gibt vollgas, dabei packt {AKTIV} {PASSIV} am Hals und drückt beherzt zu',
|
||||
4, 30, 60, '10000000-0000-0000-0000-000000000005'),
|
||||
|
||||
('30000000-0000-0000-0000-000000000041', 'Analsex in Missionarstellung und Breathplay',
|
||||
'{AKTIV} dringt in Missionarsstellung anal in {PASSIV} und gibt vollgas, dabei packt {AKTIV} {PASSIV} am Hals und drückt beherzt zu',
|
||||
4, 30, 60, '10000000-0000-0000-0000-000000000005');
|
||||
|
||||
-- strafe_benoetigt_passiv (war @CollectionTable name="strafe_benoetigtPassiv")
|
||||
INSERT IGNORE INTO strafe_benoetigt_passiv (strafe_id, werkzeug) VALUES
|
||||
('30000000-0000-0000-0000-000000000009', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000010', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000011', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000012', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000014', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000015', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000016', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000017', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000018', 'MUND'),
|
||||
('30000000-0000-0000-0000-000000000019', 'MUND'),
|
||||
('30000000-0000-0000-0000-000000000020', 'MUND'),
|
||||
('30000000-0000-0000-0000-000000000021', 'MUND'),
|
||||
('30000000-0000-0000-0000-000000000022', 'MUND'),
|
||||
('30000000-0000-0000-0000-000000000023', 'MUND'),
|
||||
('30000000-0000-0000-0000-000000000033', 'MUND'),
|
||||
('30000000-0000-0000-0000-000000000034', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000035', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000036', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000037', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000038', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000039', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000040', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000041', 'ANUS');
|
||||
|
||||
-- strafe_benoetigt_aktiv (war @CollectionTable name="strafe_benoetigtAktiv")
|
||||
INSERT IGNORE INTO strafe_benoetigt_aktiv (strafe_id, werkzeug) VALUES
|
||||
('30000000-0000-0000-0000-000000000018', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000018', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000019', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000019', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000020', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000020', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000021', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000021', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000022', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000023', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000027', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000027', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000028', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000028', 'ANUS'),
|
||||
('30000000-0000-0000-0000-000000000029', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000030', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000031', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000032', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000033', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000033', 'UMSCHNALLDILDO'),
|
||||
('30000000-0000-0000-0000-000000000034', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000034', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000035', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000035', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000036', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000036', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000037', 'VAGINA'),
|
||||
('30000000-0000-0000-0000-000000000037', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000040', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000040', 'UMSCHNALLDILDO'),
|
||||
('30000000-0000-0000-0000-000000000041', 'PENIS'),
|
||||
('30000000-0000-0000-0000-000000000041', 'UMSCHNALLDILDO');
|
||||
|
||||
|
||||
-- ── Aufgaben ─────────────────────────────────────────────────
|
||||
INSERT IGNORE INTO aufgabe (aufgabe_id, kurz_text, text, level, sekunden_von, sekunden_bis, gruppe_id) VALUES
|
||||
('40000000-0000-0000-0000-000000000001', 'Hintern präsentieren',
|
||||
'{AKTIV}, zeig {PASSIV} deinen Hintern, gib dir selber dabei ein oder zwei Klappse auf den Po',
|
||||
1, NULL, NULL, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000002', 'Hals küssen',
|
||||
'{AKTIV}, küsse den Hals von {PASSIV} leidenschaftlich',
|
||||
1, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000003', 'Bauchnabel küssen',
|
||||
'{AKTIV}, zeichne mit Küssen den Bauchnabel von {PASSIV} nach',
|
||||
1, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000004', 'Ohren knabbern',
|
||||
'{AKTIV}, knabber leidenschaftlich an den Ohrläppchen von {PASSIV}',
|
||||
1, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000005', 'Berühren ohne anfassen',
|
||||
'{AKTIV}, berühre den gesamten Körper von {PASSIV} ohne die Hände zu verwenden',
|
||||
2, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000006', 'Nacken küssen',
|
||||
'{PASSIV} sitzt vor {AKTIV}, {AKTIV} küsste leidenschaftlich den Nacken von {PASSIV}',
|
||||
1, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000007', 'Brust küssen',
|
||||
'{AKTIV}, küsse die Brust von {PASSIV} ohne die Nippel zu berühren',
|
||||
1, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000008', 'Nippel verwöhnen',
|
||||
'{AKTIV}, verwöhne die Nippel von {PASSIV} mit Küssen',
|
||||
2, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000009', 'Hintern küssen',
|
||||
'{AKTIV}, küsse den Hintern von {PASSIV} ohne den Anus zu berühren',
|
||||
1, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000010', 'Intimkuss durch Unterwäsche',
|
||||
'{AKTIV}, küsse den Intimbereich von {PASSIV} durch die Unterwäsche',
|
||||
2, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000011', 'Brustmassage',
|
||||
'{AKTIV}, massiere die Brust von {PASSIV} leidenschaftlich',
|
||||
1, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000012', 'Hinternmassage',
|
||||
'{AKTIV}, massiere den Hintern von {PASSIV} leidenschaftlich',
|
||||
1, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000013', 'Rückenmassage',
|
||||
'{AKTIV}, massiere den Rücken von {PASSIV} leidenschaftlich',
|
||||
1, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000014', 'Oberschenkelmassage',
|
||||
'{AKTIV}, massiere die Oberschenkel von {PASSIV} leidenschaftlich',
|
||||
1, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000015', 'Klitoris mit Vibrator verwöhnen',
|
||||
'{AKTIV}, verwöhne die Klitoris von {PASSIV} mit einem Vibrator',
|
||||
3, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000016', 'Cunnilingus und Finger in Vagina',
|
||||
'{AKTIV}, verwöhne die Klitoris von {PASSIV} mit dem Mund, führe dabei einen bis zwei Finger in die Vagina von {PASSIV} ein',
|
||||
3, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000017', 'Klitoris mit Fingern verwöhnen und Finger in Vagina',
|
||||
'{AKTIV}, verwöhne die Klitoris von {PASSIV} mit der Hand, führe dabei einen bis zwei Finger in die Vagina von {PASSIV} ein',
|
||||
4, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000018', 'Eichel mit Vibrator verwöhnen',
|
||||
'{AKTIV}, verwöhne die Eichel von {PASSIV} mit einem Vibrator',
|
||||
3, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000019', 'Felatio',
|
||||
'{AKTIV}, verwöhne die Eichel von {PASSIV} mit dem Mund',
|
||||
3, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000020', 'Handjob',
|
||||
'{AKTIV}, verwöhne die Eichel von {PASSIV} mit der Hand',
|
||||
3, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000021', 'Facesitting',
|
||||
'{AKTIV} liegt auf dem Rücken, {PASSIV} sitzt auf seinem Gesicht. {AKTIV}, verwöhne die Vagina von {PASSIV} mit dem Mund',
|
||||
4, 60, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000022', '69er-Position',
|
||||
'69er-Zeit: {AKTIV} liegt oben. {PASSIV}, falls du verschlossen bist, ziehe einen Strap on an, damit {AKTIV} auch was zu tun hat.',
|
||||
4, 60, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000023', 'Kleiner Dildo vaginal',
|
||||
'{AKTIV}, führe {PASSIV} einen kleinen Dildo vaginal ein und verwöhne {PASSIV} durch langsame Bewegungen mit selbigem',
|
||||
3, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000024', 'Großer Dildo vaginal',
|
||||
'{AKTIV}, führe {PASSIV} einen großen Dildo vaginal ein und verwöhne {PASSIV} durch langsame Bewegungen mit selbigem',
|
||||
4, 30, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000025', 'Großer Dildo vaginal schnell',
|
||||
'{AKTIV}, führe {PASSIV} einen großen Dildo vaginal ein und bewege selbigen möglichst schnell rein und raus',
|
||||
5, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000026', 'Missionarstellung langsam',
|
||||
'{AKTIV} dringt in Missionarstellung in {PASSIV} ein und verwöhnt {PASSIV} mit langsamen Bewegungen',
|
||||
3, 60, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000027', 'Missionarstellung schnell',
|
||||
'{AKTIV} dringt in Missionarstellung in {PASSIV} ein und verwöhnt {PASSIV} mit schnellen Bewegungen',
|
||||
4, 30, 90, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000028', 'Missionarstellung Vollgas',
|
||||
'{AKTIV} dringt in Missionarstellung in {PASSIV} ein und gibt vollgas',
|
||||
5, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000029', 'Reiterstellung langsam',
|
||||
'{PASSIV} setzt sich in Reiterstellung auf {AKTIV}. {PASSIV} bestimmt das Tempo',
|
||||
3, 60, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000030', 'Reiterstellung schnell',
|
||||
'{PASSIV} setzt sich in Reiterstellung auf {AKTIV}. {PASSIV} versucht das Tempo hoch zu halten',
|
||||
4, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000031', 'Reiterstellung vollgas',
|
||||
'{PASSIV} setzt sich in Reiterstellung auf {AKTIV} und gibt vollgas',
|
||||
5, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000032', 'Doggystyle langsam',
|
||||
'{AKTIV} dringt in Hundestellung in {PASSIV} ein und verwöhnt {PASSIV} mit langsamen Bewegungen',
|
||||
3, 60, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000033', 'Doggystyle schnell',
|
||||
'{AKTIV} dringt in Hundestellung in {PASSIV} ein und verwöhnt {PASSIV} mit schnellen Bewegungen',
|
||||
4, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000034', 'Doggystyle vollgas',
|
||||
'{AKTIV} dringt in Hundestellung in {PASSIV} ein und gibt vollgas',
|
||||
5, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000035', 'Doggystyle vollgas keinen Mucks',
|
||||
'{AKTIV} dringt in Hundestellung in {PASSIV} ein und gibt vollgas. {PASSIV} darf dabei keinen Laut von sich geben.',
|
||||
5, 30, 60, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000036', 'Doggystyle Tempo bestimmt die ''gefickte'' Person',
|
||||
'{AKTIV} dringt in Hundestellung in {PASSIV} ein. {AKTIV} hält still und {PASSIV} gibt das Tempo vor',
|
||||
3, 60, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000037', 'Löffelchen langsam',
|
||||
'{AKTIV} dringt in Löffelchenstellung in {PASSIV} ein und verwöhnt {PASSIV} mit langsamen Bewegungen',
|
||||
3, 60, 180, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000038', 'Löffelchen schnell',
|
||||
'{AKTIV} dringt in Löffelchenstellung in {PASSIV} ein und verwöhnt {PASSIV} mit schnellen Bewegungen',
|
||||
4, 60, 120, '10000000-0000-0000-0000-000000000006'),
|
||||
|
||||
('40000000-0000-0000-0000-000000000039', 'Löffelchen vollgas',
|
||||
'{AKTIV} dringt in Löffelchenstellung in {PASSIV} ein und gibt vollgas',
|
||||
5, 30, 60, '10000000-0000-0000-0000-000000000006');
|
||||
|
||||
-- aufgabe_benoetigt_aktiv (war @CollectionTable name="aufgabe_benoetigtAktiv")
|
||||
INSERT IGNORE INTO aufgabe_benoetigt_aktiv (aufgabe_id, werkzeug) VALUES
|
||||
('40000000-0000-0000-0000-000000000002', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000003', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000004', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000006', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000007', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000008', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000009', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000010', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000016', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000019', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000021', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000022', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000022', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000026', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000026', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000027', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000027', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000028', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000028', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000029', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000029', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000030', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000030', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000031', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000031', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000032', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000032', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000033', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000033', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000034', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000034', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000035', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000035', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000036', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000036', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000037', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000037', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000038', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000038', 'UMSCHNALLDILDO'),
|
||||
('40000000-0000-0000-0000-000000000039', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000039', 'UMSCHNALLDILDO');
|
||||
|
||||
-- aufgabe_benoetigt_passiv (war @CollectionTable name="aufgabe_benoetigtPassiv")
|
||||
INSERT IGNORE INTO aufgabe_benoetigt_passiv (aufgabe_id, werkzeug) VALUES
|
||||
('40000000-0000-0000-0000-000000000015', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000016', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000017', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000018', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000019', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000020', 'PENIS'),
|
||||
('40000000-0000-0000-0000-000000000021', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000022', 'MUND'),
|
||||
('40000000-0000-0000-0000-000000000023', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000024', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000025', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000026', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000027', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000028', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000029', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000030', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000031', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000032', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000033', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000034', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000035', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000036', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000037', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000038', 'VAGINA'),
|
||||
('40000000-0000-0000-0000-000000000039', 'VAGINA');
|
||||
504
xxxthegame/src/main/resources/sql/testdaten.sql
Normal file
504
xxxthegame/src/main/resources/sql/testdaten.sql
Normal file
@@ -0,0 +1,504 @@
|
||||
-- =============================================================
|
||||
-- XXX The Game – Testdaten
|
||||
-- =============================================================
|
||||
-- Passwort für alle User: Test1234!
|
||||
-- SHA-256("Test1234!") = 11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a
|
||||
-- Hinweis: Login erwartet SHA-256-Hash vom Client
|
||||
-- =============================================================
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- Aufräumen (Reihenfolge wegen FK)
|
||||
DELETE FROM kommentar_like;
|
||||
DELETE FROM kommentar;
|
||||
DELETE FROM pinnwand_like;
|
||||
DELETE FROM pinnwand_eintrag;
|
||||
DELETE FROM feed_post_vote;
|
||||
DELETE FROM feed_post_option;
|
||||
DELETE FROM feed_post_like;
|
||||
DELETE FROM feed_post;
|
||||
DELETE FROM umfrage_stimme;
|
||||
DELETE FROM umfrage_option;
|
||||
DELETE FROM gruppe_beitrag_like;
|
||||
DELETE FROM gruppe_beitrag;
|
||||
DELETE FROM beitrittsanfrage;
|
||||
DELETE FROM gruppe_mitglied;
|
||||
DELETE FROM gruppe;
|
||||
DELETE FROM profile_image_like;
|
||||
DELETE FROM profile_image;
|
||||
DELETE FROM friendship;
|
||||
DELETE FROM registration;
|
||||
DELETE FROM `user`;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- =============================================================
|
||||
-- BENUTZER (5 User mit unterschiedlichen Profilen)
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO `user` (
|
||||
user_id, name, email, password, geburtsdatum,
|
||||
groesse, gewicht, geschlecht, neigung, beziehungsstatus, beschreibung,
|
||||
lockee_xp, keyholder_xp, bdsm_xp,
|
||||
sichtbarkeit_grunddaten, sichtbarkeit_galerie, sichtbarkeit_freunde,
|
||||
sichtbarkeit_feed, sichtbarkeit_pinnwand, sichtbarkeit_xp, sichtbarkeit_lockhistorie
|
||||
) VALUES
|
||||
|
||||
-- 1. MaxMuster – dominant, Single
|
||||
('11111111-1111-1111-1111-000000000001',
|
||||
'MaxMuster', 'max@test.de',
|
||||
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
|
||||
'1990-05-15',
|
||||
182, 80, 'MAENNLICH', 'DOMINANT', 'SINGLE',
|
||||
'Erfahrener Keyholder, der auf striktes aber faires Spiel steht. Immer offen für neue Spielpartner.',
|
||||
120, 850, 300,
|
||||
'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE'),
|
||||
|
||||
-- 2. LisaLust – devot, Single
|
||||
('11111111-1111-1111-1111-000000000002',
|
||||
'LisaLust', 'lisa@test.de',
|
||||
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
|
||||
'1995-08-22',
|
||||
165, 58, 'WEIBLICH', 'DEVOT', 'SINGLE',
|
||||
'Neugierigie Lockee auf der Suche nach einem verlässlichen Keyholder. Mag lange Sperren und herausfordernde Aufgaben.',
|
||||
740, 0, 150,
|
||||
'ALLE', 'NUR_FREUNDE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'NUR_FREUNDE'),
|
||||
|
||||
-- 3. SamSwitcher – Switcher, in Beziehung
|
||||
('11111111-1111-1111-1111-000000000003',
|
||||
'SamSwitcher', 'sam@test.de',
|
||||
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
|
||||
'1988-11-03',
|
||||
175, 70, 'DIVERS', 'SWITCHER', 'IN_EINER_BEZIEHUNG',
|
||||
'Mal oben, mal unten – kommt auf die Stimmung an. Spiele gerne mit meinem Partner zusammen.',
|
||||
430, 390, 600,
|
||||
'ALLE', 'ALLE', 'ALLE', 'NUR_FREUNDE', 'ALLE', 'NUR_FREUNDE', 'ALLE'),
|
||||
|
||||
-- 4. KajaKette – eher devot, Single
|
||||
('11111111-1111-1111-1111-000000000004',
|
||||
'KajaKette', 'kaja@test.de',
|
||||
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
|
||||
'1998-02-14',
|
||||
170, 62, 'WEIBLICH', 'EHER_DEVOT', 'SINGLE',
|
||||
'Chastity-Enthusiastin mit Fokus auf Community-Locks. Schreibe gerne auf Pinnwände!',
|
||||
920, 50, 80,
|
||||
'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE', 'ALLE'),
|
||||
|
||||
-- 5. TomTop – eher dominant, verheiratet
|
||||
('11111111-1111-1111-1111-000000000005',
|
||||
'TomTop', 'tom@test.de',
|
||||
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
|
||||
'1985-07-30',
|
||||
178, 85, 'MAENNLICH', 'EHER_DOMINANT', 'VERHEIRATET',
|
||||
'Verheiratet, spielen als Paar. Biete Keyholder-Service für seriöse Anfragen.',
|
||||
200, 560, 410,
|
||||
'ALLE', 'NUR_FREUNDE', 'NUR_FREUNDE', 'NUR_FREUNDE', 'ALLE', 'ALLE', 'NUR_FREUNDE');
|
||||
|
||||
-- =============================================================
|
||||
-- NICHT AKTIVIERTE REGISTRIERUNG (für Registrierungs-Tests)
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO registration (
|
||||
registration_id, name, email, password, activated, activation_code, geburtsdatum
|
||||
) VALUES
|
||||
('99999999-9999-9999-9999-000000000001',
|
||||
'NeuerUser', 'neu@test.de',
|
||||
'11a1162b984f8cf531e07d9bde6e27f26d6e9c0a2c4c52a6c1f0e2e79cd4e4a',
|
||||
FALSE, '347821', '2000-01-01');
|
||||
|
||||
-- =============================================================
|
||||
-- FREUNDSCHAFTEN
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO friendship (friendship_id, sender_id, receiver_id, status, created_at) VALUES
|
||||
-- Max ↔ Lisa (akzeptiert)
|
||||
('22222222-2222-2222-2222-000000000001',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'ACCEPTED', '2025-11-01 10:00:00'),
|
||||
-- Max ↔ Sam (akzeptiert)
|
||||
('22222222-2222-2222-2222-000000000002',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'ACCEPTED', '2025-11-15 14:30:00'),
|
||||
-- Lisa ↔ Kaja (akzeptiert)
|
||||
('22222222-2222-2222-2222-000000000003',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'ACCEPTED', '2025-12-03 09:15:00'),
|
||||
-- Tom → Kaja (ausstehend)
|
||||
('22222222-2222-2222-2222-000000000004',
|
||||
'11111111-1111-1111-1111-000000000005',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'PENDING', '2026-01-10 18:45:00'),
|
||||
-- Sam ↔ Kaja (akzeptiert)
|
||||
('22222222-2222-2222-2222-000000000005',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'ACCEPTED', '2026-01-20 11:00:00');
|
||||
|
||||
-- =============================================================
|
||||
-- PINNWAND-EINTRÄGE
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO pinnwand_eintrag (eintrag_id, profil_user_id, author_id, text, created_at) VALUES
|
||||
-- Auf Lisas Pinnwand
|
||||
('33333333-3333-3333-3333-000000000001',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'Hey Lisa! Schön, dich hier zu sehen. Viel Spaß beim Spielen 🔒',
|
||||
'2025-12-10 16:00:00'),
|
||||
('33333333-3333-3333-3333-000000000002',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'Wir sollten mal ein gemeinsames Lock starten! Meld dich 😊',
|
||||
'2026-01-05 12:30:00'),
|
||||
-- Auf Maxs Pinnwand
|
||||
('33333333-3333-3333-3333-000000000003',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'Danke für den tollen Keyholder-Service letzte Woche!',
|
||||
'2026-01-08 20:00:00'),
|
||||
-- Auf Kajas Pinnwand
|
||||
('33333333-3333-3333-3333-000000000004',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'Kaja, du bist die Community-Queen! Immer so aktiv hier.',
|
||||
'2026-02-14 09:00:00');
|
||||
|
||||
-- Pinnwand-Likes
|
||||
INSERT INTO pinnwand_like (like_id, eintrag_id, user_id, liked_at) VALUES
|
||||
('33333333-3333-3333-3333-000000000101',
|
||||
'33333333-3333-3333-3333-000000000001',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'2025-12-10 16:05:00'),
|
||||
('33333333-3333-3333-3333-000000000102',
|
||||
'33333333-3333-3333-3333-000000000002',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'2026-01-05 13:00:00'),
|
||||
('33333333-3333-3333-3333-000000000103',
|
||||
'33333333-3333-3333-3333-000000000003',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'2026-01-09 10:00:00');
|
||||
|
||||
-- =============================================================
|
||||
-- KOMMENTARE
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO kommentar (kommentar_id, author_id, target_type, target_id, text, created_at) VALUES
|
||||
-- Kommentar auf Pinnwand-Eintrag
|
||||
('44444444-4444-4444-4444-000000000001',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'PINNWAND',
|
||||
'33333333-3333-3333-3333-000000000001',
|
||||
'Danke Max! Ich freu mich auch 😊',
|
||||
'2025-12-10 17:00:00'),
|
||||
('44444444-4444-4444-4444-000000000002',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'PINNWAND',
|
||||
'33333333-3333-3333-3333-000000000001',
|
||||
'+1, willkommen in der Community!',
|
||||
'2025-12-10 18:30:00'),
|
||||
-- Reply auf Kommentar
|
||||
('44444444-4444-4444-4444-000000000003',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'KOMMENTAR',
|
||||
'44444444-4444-4444-4444-000000000001',
|
||||
'Na logo! Wir machen das 😄',
|
||||
'2025-12-10 17:15:00');
|
||||
|
||||
-- Kommentar-Likes
|
||||
INSERT INTO kommentar_like (like_id, kommentar_id, user_id, liked_at) VALUES
|
||||
('44444444-4444-4444-4444-000000000101',
|
||||
'44444444-4444-4444-4444-000000000001',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'2025-12-10 17:10:00'),
|
||||
('44444444-4444-4444-4444-000000000102',
|
||||
'44444444-4444-4444-4444-000000000002',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'2025-12-10 19:00:00');
|
||||
|
||||
-- =============================================================
|
||||
-- FEED-POSTS (Text + Umfrage)
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO feed_post (post_id, author_id, text, beitrag_typ, multi_choice, is_public, created_at) VALUES
|
||||
-- Öffentlicher Text-Post von Max
|
||||
('55555555-5555-5555-5555-000000000001',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'Wer hat Lust auf ein Cardlock-Turnier nächsten Monat? Community vs. Keyholder! 🃏',
|
||||
'TEXT', NULL, TRUE, '2026-02-01 10:00:00'),
|
||||
|
||||
-- Öffentlicher Text-Post von Lisa
|
||||
('55555555-5555-5555-5555-000000000002',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'48 Stunden geschafft! Das war mein bisher längstes Lock. Ich bin so stolz auf mich! 🔐✨',
|
||||
'TEXT', NULL, TRUE, '2026-02-05 14:30:00'),
|
||||
|
||||
-- Öffentliche Umfrage von Kaja (Single-Choice)
|
||||
('55555555-5555-5555-5555-000000000003',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'Was bevorzugt ihr: Cardlock oder Timelock?',
|
||||
'UMFRAGE', FALSE, TRUE, '2026-02-10 09:00:00'),
|
||||
|
||||
-- Öffentliche Umfrage von Sam (Multi-Choice)
|
||||
('55555555-5555-5555-5555-000000000004',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'Welche Features wollt ihr als nächstes sehen? (Mehrfachauswahl möglich)',
|
||||
'UMFRAGE', TRUE, TRUE, '2026-02-15 20:00:00'),
|
||||
|
||||
-- Nicht-öffentlicher Post von Tom
|
||||
('55555555-5555-5555-5555-000000000005',
|
||||
'11111111-1111-1111-1111-000000000005',
|
||||
'Spielen heute Abend mit meiner Frau eine Runde BDSM. Sie darf den Keyholder spielen!',
|
||||
'TEXT', NULL, FALSE, '2026-02-20 18:00:00');
|
||||
|
||||
-- Umfrage-Optionen
|
||||
INSERT INTO feed_post_option (option_id, post_id, text, reihenfolge) VALUES
|
||||
-- Kajas Umfrage
|
||||
('55555555-5555-5555-5555-000000000101', '55555555-5555-5555-5555-000000000003', 'Cardlock – ich liebe die Ungewissheit!', 0),
|
||||
('55555555-5555-5555-5555-000000000102', '55555555-5555-5555-5555-000000000003', 'Timelock – Struktur ist alles.', 1),
|
||||
('55555555-5555-5555-5555-000000000103', '55555555-5555-5555-5555-000000000003', 'Beides gleich gerne.', 2),
|
||||
-- Sams Umfrage
|
||||
('55555555-5555-5555-5555-000000000104', '55555555-5555-5555-5555-000000000004', 'Mobile App', 0),
|
||||
('55555555-5555-5555-5555-000000000105', '55555555-5555-5555-5555-000000000004', 'Mehr Aufgaben-Vorlagen', 1),
|
||||
('55555555-5555-5555-5555-000000000106', '55555555-5555-5555-5555-000000000004', 'Dark/Light Theme Toggle', 2),
|
||||
('55555555-5555-5555-5555-000000000107', '55555555-5555-5555-5555-000000000004', 'Push-Benachrichtigungen', 3);
|
||||
|
||||
-- Umfrage-Stimmen
|
||||
INSERT INTO feed_post_vote (stimme_id, option_id, post_id, user_id) VALUES
|
||||
-- Kajas Umfrage
|
||||
('55555555-5555-5555-5555-000000000201', '55555555-5555-5555-5555-000000000101', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000001'),
|
||||
('55555555-5555-5555-5555-000000000202', '55555555-5555-5555-5555-000000000101', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000002'),
|
||||
('55555555-5555-5555-5555-000000000203', '55555555-5555-5555-5555-000000000102', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000005'),
|
||||
('55555555-5555-5555-5555-000000000204', '55555555-5555-5555-5555-000000000103', '55555555-5555-5555-5555-000000000003', '11111111-1111-1111-1111-000000000003'),
|
||||
-- Sams Umfrage (Multi-Choice)
|
||||
('55555555-5555-5555-5555-000000000205', '55555555-5555-5555-5555-000000000104', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000001'),
|
||||
('55555555-5555-5555-5555-000000000206', '55555555-5555-5555-5555-000000000105', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000001'),
|
||||
('55555555-5555-5555-5555-000000000207', '55555555-5555-5555-5555-000000000104', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000002'),
|
||||
('55555555-5555-5555-5555-000000000208', '55555555-5555-5555-5555-000000000107', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000002'),
|
||||
('55555555-5555-5555-5555-000000000209', '55555555-5555-5555-5555-000000000105', '55555555-5555-5555-5555-000000000004', '11111111-1111-1111-1111-000000000004');
|
||||
|
||||
-- Feed-Likes
|
||||
INSERT INTO feed_post_like (like_id, post_id, user_id, liked_at) VALUES
|
||||
('55555555-5555-5555-5555-000000000301', '55555555-5555-5555-5555-000000000001', '11111111-1111-1111-1111-000000000002', '2026-02-01 10:30:00'),
|
||||
('55555555-5555-5555-5555-000000000302', '55555555-5555-5555-5555-000000000001', '11111111-1111-1111-1111-000000000003', '2026-02-01 11:00:00'),
|
||||
('55555555-5555-5555-5555-000000000303', '55555555-5555-5555-5555-000000000001', '11111111-1111-1111-1111-000000000004', '2026-02-01 11:15:00'),
|
||||
('55555555-5555-5555-5555-000000000304', '55555555-5555-5555-5555-000000000002', '11111111-1111-1111-1111-000000000001', '2026-02-05 15:00:00'),
|
||||
('55555555-5555-5555-5555-000000000305', '55555555-5555-5555-5555-000000000002', '11111111-1111-1111-1111-000000000004', '2026-02-05 15:30:00'),
|
||||
('55555555-5555-5555-5555-000000000306', '55555555-5555-5555-5555-000000000002', '11111111-1111-1111-1111-000000000003', '2026-02-05 16:00:00');
|
||||
|
||||
-- Kommentare unter Feed-Posts
|
||||
INSERT INTO kommentar (kommentar_id, author_id, target_type, target_id, text, created_at) VALUES
|
||||
('66666666-6666-6666-6666-000000000001',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'FEED_POST',
|
||||
'55555555-5555-5555-5555-000000000001',
|
||||
'Bin dabei! Wann genau? 🙋♀️',
|
||||
'2026-02-01 11:00:00'),
|
||||
('66666666-6666-6666-6666-000000000002',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'FEED_POST',
|
||||
'55555555-5555-5555-5555-000000000001',
|
||||
'Klingt mega! Ich schlage vor: 1 Woche Mindestlaufzeit.',
|
||||
'2026-02-01 11:30:00'),
|
||||
('66666666-6666-6666-6666-000000000003',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'FEED_POST',
|
||||
'55555555-5555-5555-5555-000000000002',
|
||||
'Respekt! 48h ist eine echte Leistung 👏',
|
||||
'2026-02-05 15:00:00');
|
||||
|
||||
-- =============================================================
|
||||
-- GRUPPEN
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO gruppe (gruppe_id, name, beschreibung, bild, is_private, created_at, created_by_user_id) VALUES
|
||||
-- Öffentliche Gruppe
|
||||
('77777777-7777-7777-7777-000000000001',
|
||||
'Cardlock Community',
|
||||
'Die Gruppe für alle Cardlock-Fans! Hier tauschen wir Erfahrungen aus, veranstalten Turniere und helfen Neulingen beim Einstieg.',
|
||||
NULL, FALSE, '2025-10-01 12:00:00',
|
||||
'11111111-1111-1111-1111-000000000001'),
|
||||
|
||||
-- Private Gruppe
|
||||
('77777777-7777-7777-7777-000000000002',
|
||||
'Keyholder-Stammtisch',
|
||||
'Privater Austausch unter erfahrenen Keyholdern. Nur auf Einladung.',
|
||||
NULL, TRUE, '2025-11-15 18:00:00',
|
||||
'11111111-1111-1111-1111-000000000005'),
|
||||
|
||||
-- Öffentliche Gruppe
|
||||
('77777777-7777-7777-7777-000000000003',
|
||||
'Anfänger & Fragen',
|
||||
'Neuling? Frag einfach! Hier ist jede Frage willkommen. Keine Scheu.',
|
||||
NULL, FALSE, '2026-01-01 00:00:00',
|
||||
'11111111-1111-1111-1111-000000000004');
|
||||
|
||||
-- =============================================================
|
||||
-- GRUPPENMITGLIEDER
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO gruppe_mitglied (mitglied_id, gruppe_id, user_id, rolle, joined_at) VALUES
|
||||
-- Cardlock Community
|
||||
('77777777-7777-7777-7777-000000000101', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000001', 'ADMIN', '2025-10-01 12:00:00'),
|
||||
('77777777-7777-7777-7777-000000000102', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000002', 'MITGLIED', '2025-10-05 09:00:00'),
|
||||
('77777777-7777-7777-7777-000000000103', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000003', 'MITGLIED', '2025-10-10 14:00:00'),
|
||||
('77777777-7777-7777-7777-000000000104', '77777777-7777-7777-7777-000000000001', '11111111-1111-1111-1111-000000000004', 'MITGLIED', '2025-10-20 11:00:00'),
|
||||
-- Keyholder-Stammtisch
|
||||
('77777777-7777-7777-7777-000000000105', '77777777-7777-7777-7777-000000000002', '11111111-1111-1111-1111-000000000005', 'ADMIN', '2025-11-15 18:00:00'),
|
||||
('77777777-7777-7777-7777-000000000106', '77777777-7777-7777-7777-000000000002', '11111111-1111-1111-1111-000000000001', 'MITGLIED', '2025-11-20 10:00:00'),
|
||||
-- Anfänger & Fragen
|
||||
('77777777-7777-7777-7777-000000000107', '77777777-7777-7777-7777-000000000003', '11111111-1111-1111-1111-000000000004', 'ADMIN', '2026-01-01 00:00:00'),
|
||||
('77777777-7777-7777-7777-000000000108', '77777777-7777-7777-7777-000000000003', '11111111-1111-1111-1111-000000000002', 'MITGLIED', '2026-01-03 08:00:00'),
|
||||
('77777777-7777-7777-7777-000000000109', '77777777-7777-7777-7777-000000000003', '11111111-1111-1111-1111-000000000003', 'MITGLIED', '2026-01-05 12:00:00');
|
||||
|
||||
-- Ausstehende Beitrittsanfrage zur privaten Gruppe
|
||||
INSERT INTO beitrittsanfrage (anfrage_id, gruppe_id, user_id, nachricht, angefragt_at, status) VALUES
|
||||
('77777777-7777-7777-7777-000000000201',
|
||||
'77777777-7777-7777-7777-000000000002',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'Hallo! Ich bin seit 2 Jahren aktiver Keyholder und würde gerne dazugehören.',
|
||||
'2026-02-01 15:00:00', 'AUSSTEHEND'),
|
||||
('77777777-7777-7777-7777-000000000202',
|
||||
'77777777-7777-7777-7777-000000000002',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'Bitte nehmt mich auf! Habe schon ein paar Monate Erfahrung als Keyholderin.',
|
||||
'2026-02-10 09:00:00', 'ABGELEHNT');
|
||||
|
||||
-- =============================================================
|
||||
-- GRUPPEN-BEITRÄGE (Text + Umfrage)
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO gruppe_beitrag (beitrag_id, gruppe_id, author_id, beitrag_typ, text, multi_choice, bild, created_at) VALUES
|
||||
-- Cardlock Community
|
||||
('88888888-8888-8888-8888-000000000001',
|
||||
'77777777-7777-7777-7777-000000000001',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'TEXT',
|
||||
'Willkommen in der Cardlock Community! Stellt euch kurz vor und erzählt, wie ihr zum Cardlock gekommen seid.',
|
||||
NULL, NULL, '2025-10-01 12:05:00'),
|
||||
|
||||
('88888888-8888-8888-8888-000000000002',
|
||||
'77777777-7777-7777-7777-000000000001',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'TEXT',
|
||||
'Ich bin Lisa und liebe Cardlocks seit über einem Jahr! Mein Rekord sind 5 Tage – habt ihr Tipps für längere Sperren?',
|
||||
NULL, NULL, '2025-10-05 10:00:00'),
|
||||
|
||||
('88888888-8888-8888-8888-000000000003',
|
||||
'77777777-7777-7777-7777-000000000001',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'UMFRAGE',
|
||||
'Wie viele Karten startet ihr typischerweise mit?',
|
||||
FALSE, NULL, '2025-10-20 14:00:00'),
|
||||
|
||||
-- Anfänger & Fragen
|
||||
('88888888-8888-8888-8888-000000000004',
|
||||
'77777777-7777-7777-7777-000000000003',
|
||||
'11111111-1111-1111-1111-000000000002',
|
||||
'TEXT',
|
||||
'Frage: Wie erkläre ich Cardlocks am besten meinem Partner, der noch nie davon gehört hat?',
|
||||
NULL, NULL, '2026-01-10 19:00:00'),
|
||||
|
||||
('88888888-8888-8888-8888-000000000005',
|
||||
'77777777-7777-7777-7777-000000000003',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'TEXT',
|
||||
'Gute Frage! Ich würde empfehlen, erst mit einem kurzen Timelock anzufangen. So kann der Partner das Grundkonzept verstehen, ohne direkt mit der Karten-Mechanik überfordert zu werden.',
|
||||
NULL, NULL, '2026-01-10 19:30:00');
|
||||
|
||||
-- Umfrage-Optionen für Gruppen-Beitrag
|
||||
INSERT INTO umfrage_option (option_id, beitrag_id, text, reihenfolge) VALUES
|
||||
('88888888-8888-8888-8888-000000000101', '88888888-8888-8888-8888-000000000003', 'Unter 20 Karten', 0),
|
||||
('88888888-8888-8888-8888-000000000102', '88888888-8888-8888-8888-000000000003', '20–40 Karten', 1),
|
||||
('88888888-8888-8888-8888-000000000103', '88888888-8888-8888-8888-000000000003', '40–60 Karten', 2),
|
||||
('88888888-8888-8888-8888-000000000104', '88888888-8888-8888-8888-000000000003', 'Über 60 Karten', 3);
|
||||
|
||||
-- Umfrage-Stimmen (Gruppen)
|
||||
INSERT INTO umfrage_stimme (stimme_id, option_id, beitrag_id, user_id) VALUES
|
||||
('88888888-8888-8888-8888-000000000201', '88888888-8888-8888-8888-000000000101', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000002'),
|
||||
('88888888-8888-8888-8888-000000000202', '88888888-8888-8888-8888-000000000102', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000001'),
|
||||
('88888888-8888-8888-8888-000000000203', '88888888-8888-8888-8888-000000000102', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000003'),
|
||||
('88888888-8888-8888-8888-000000000204', '88888888-8888-8888-8888-000000000103', '88888888-8888-8888-8888-000000000003', '11111111-1111-1111-1111-000000000004');
|
||||
|
||||
-- Gruppen-Beitrag-Likes
|
||||
INSERT INTO gruppe_beitrag_like (like_id, beitrag_id, user_id, liked_at) VALUES
|
||||
('88888888-8888-8888-8888-000000000301', '88888888-8888-8888-8888-000000000001', '11111111-1111-1111-1111-000000000002', '2025-10-01 12:10:00'),
|
||||
('88888888-8888-8888-8888-000000000302', '88888888-8888-8888-8888-000000000001', '11111111-1111-1111-1111-000000000003', '2025-10-01 13:00:00'),
|
||||
('88888888-8888-8888-8888-000000000303', '88888888-8888-8888-8888-000000000001', '11111111-1111-1111-1111-000000000004', '2025-10-01 14:00:00'),
|
||||
('88888888-8888-8888-8888-000000000304', '88888888-8888-8888-8888-000000000002', '11111111-1111-1111-1111-000000000001', '2025-10-05 10:30:00'),
|
||||
('88888888-8888-8888-8888-000000000305', '88888888-8888-8888-8888-000000000002', '11111111-1111-1111-1111-000000000004', '2025-10-05 11:00:00'),
|
||||
('88888888-8888-8888-8888-000000000306', '88888888-8888-8888-8888-000000000005', '11111111-1111-1111-1111-000000000002', '2026-01-10 19:45:00'),
|
||||
('88888888-8888-8888-8888-000000000307', '88888888-8888-8888-8888-000000000005', '11111111-1111-1111-1111-000000000004', '2026-01-10 20:00:00');
|
||||
|
||||
-- Kommentare auf Gruppen-Beiträge
|
||||
INSERT INTO kommentar (kommentar_id, author_id, target_type, target_id, text, created_at) VALUES
|
||||
('99999999-0000-0000-0000-000000000001',
|
||||
'11111111-1111-1111-1111-000000000003',
|
||||
'GROUP_POST',
|
||||
'88888888-8888-8888-8888-000000000002',
|
||||
'Hi Lisa! Mein Tipp: Fang mit mehr Green Cards an als du denkst. Du wirst es brauchen 😄',
|
||||
'2025-10-05 11:00:00'),
|
||||
('99999999-0000-0000-0000-000000000002',
|
||||
'11111111-1111-1111-1111-000000000001',
|
||||
'GROUP_POST',
|
||||
'88888888-8888-8888-8888-000000000002',
|
||||
'Mentale Vorbereitung ist alles. Schreib dir vorher auf, warum du es tust.',
|
||||
'2025-10-05 12:00:00'),
|
||||
('99999999-0000-0000-0000-000000000003',
|
||||
'11111111-1111-1111-1111-000000000004',
|
||||
'GROUP_POST',
|
||||
'88888888-8888-8888-8888-000000000004',
|
||||
'Ich würde sagen: zeig ihm/ihr einfach die App! Das visuelle Konzept erklärt sich fast von selbst.',
|
||||
'2026-01-10 19:15:00');
|
||||
|
||||
-- =============================================================
|
||||
-- CHASTITY LOCK (ein aktives Cardlock: Lisa gesperrt von Max)
|
||||
-- =============================================================
|
||||
|
||||
INSERT INTO current_lock (
|
||||
lock_id, lock_type, name, lockee, keyholder,
|
||||
test_lock, requires_verification,
|
||||
unlock_code_length, unlock_code,
|
||||
start_time, unlock_time,
|
||||
hygine_opening_duration_minutes, hygine_opening_everyminites,
|
||||
task_mode,
|
||||
keyholder_requested_unlock, emergency_auto_unlocked,
|
||||
-- CARDLOCK-spezifisch
|
||||
initial_cards, pick_every_minute, accumulate_picks,
|
||||
show_remaining_cards, open_picks,
|
||||
available_cards
|
||||
) VALUES (
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-000000000001',
|
||||
'CARDLOCK',
|
||||
'Lisas Frühlings-Lock',
|
||||
'11111111-1111-1111-1111-000000000002', -- lockee: Lisa
|
||||
'11111111-1111-1111-1111-000000000001', -- keyholder: Max
|
||||
FALSE, FALSE,
|
||||
6, NULL,
|
||||
'2026-03-20 10:00:00', NULL,
|
||||
30, 1440, -- Hygiene alle 24h, 30 Min offen
|
||||
'KEYHOLDER',
|
||||
FALSE, FALSE,
|
||||
-- 30 Karten: 5×GREEN, 15×RED, 5×YELLOW, 3×TASK, 2×FREEZE
|
||||
'["GREEN","GREEN","GREEN","GREEN","GREEN","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","YELLOW","YELLOW","YELLOW","YELLOW","YELLOW","TASK","TASK","TASK","FREEZE","FREEZE"]',
|
||||
240, FALSE, -- Karte alle 4h ziehen, kein Akkumulieren
|
||||
TRUE, 0,
|
||||
'["GREEN","GREEN","GREEN","GREEN","GREEN","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","RED","YELLOW","YELLOW","YELLOW","YELLOW","YELLOW","TASK","TASK","TASK","FREEZE","FREEZE"]'
|
||||
);
|
||||
|
||||
-- =============================================================
|
||||
-- FERTIG
|
||||
-- =============================================================
|
||||
-- Überblick:
|
||||
-- 5 User (max@test.de, lisa@test.de, sam@test.de, kaja@test.de, tom@test.de)
|
||||
-- 1 nicht aktivierte Registrierung (neu@test.de, Code: 347821)
|
||||
-- 5 Freundschaften (4 akzeptiert, 1 ausstehend)
|
||||
-- 4 Pinnwand-Einträge + 3 Likes
|
||||
-- 3 Kommentare auf Pinnwand + 3 auf Feed + 3 auf Gruppen-Beiträge
|
||||
-- 5 Feed-Posts (3 Text, 2 Umfragen) + 6 Likes
|
||||
-- 3 Gruppen (2 öffentlich, 1 privat) mit je 4-6 Mitgliedern
|
||||
-- 5 Gruppen-Beiträge (4 Text, 1 Umfrage) + 7 Likes
|
||||
-- 1 aktives Cardlock (Lisa ← Max)
|
||||
-- =============================================================
|
||||
@@ -194,6 +194,15 @@
|
||||
font-size:0.78rem; color:var(--color-muted); line-height:1.6; }
|
||||
.placeholder-hint code { background:rgba(233,69,96,0.12); color:var(--color-primary);
|
||||
border-radius:3px; padding:0.05rem 0.3rem; font-size:0.75rem; }
|
||||
#iTextAC { position:fixed; z-index:9999; background:var(--color-surface,#1e1e2e);
|
||||
border:1px solid var(--color-border,#444); border-radius:6px;
|
||||
box-shadow:0 4px 14px rgba(0,0,0,.5); display:none; overflow:hidden; min-width:180px; max-height:280px; overflow-y:auto; }
|
||||
.ac-item { padding:0.45rem 0.9rem; cursor:pointer; font-size:0.88rem;
|
||||
font-family:monospace; color:var(--color-text,#cdd6f4); user-select:none; }
|
||||
.ac-item:hover, .ac-item-active { background:var(--color-primary,#cba6f7); color:#1e1e2e; }
|
||||
.ac-separator { padding:0.2rem 0.7rem; font-size:0.72rem; color:var(--color-muted);
|
||||
text-transform:uppercase; letter-spacing:0.05em; background:rgba(255,255,255,0.04);
|
||||
pointer-events:none; border-top:1px solid rgba(136,136,136,0.2); margin-top:2px; }
|
||||
.modal-two-col { display:flex; gap:0.75rem; }
|
||||
.modal-two-col > * { flex:1; }
|
||||
.werkzeug-checks { display:flex; flex-wrap:nowrap; gap:0.25rem; margin-top:0.5rem; }
|
||||
@@ -315,6 +324,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="iText" rows="4" maxlength="4000" placeholder="Ausführliche Beschreibung…"></textarea>
|
||||
<div id="iTextAC"></div>
|
||||
|
||||
<div id="iGeschlechtRow">
|
||||
<label>Geschlecht der Person die kommt *</label>
|
||||
@@ -430,6 +440,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Verschieben-Modal -->
|
||||
<div class="modal-backdrop" id="moveModal">
|
||||
<div class="modal" style="max-width:400px;">
|
||||
<h2>In andere Gruppe verschieben</h2>
|
||||
<p id="moveModalItemName" style="font-size:0.9rem;color:var(--color-muted);margin:0 0 1rem 0;"></p>
|
||||
<div class="form-group">
|
||||
<label for="moveModalSelect">Zielgruppe</label>
|
||||
<select id="moveModalSelect" class="form-control" style="height:2.6rem;font-size:1rem;"></select>
|
||||
</div>
|
||||
<p id="moveModalError" style="color:var(--color-danger);font-size:0.85rem;min-height:1.2em;margin:0.5rem 0 0 0;"></p>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-cancel" id="moveModalCancel">Abbrechen</button>
|
||||
<button class="btn-save" id="moveModalOk">Verschieben</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
|
||||
@@ -926,7 +953,7 @@ function renderAdminGruppen(gruppen) {
|
||||
${renderSubSection('Aufgaben', sortByLevelThenName(g.aufgaben || []), 'aufgabe', renderAufgabe, g.gruppenId)}
|
||||
${renderSubSection('Strafen', sortByLevelThenName(g.strafen || []), 'strafe', renderStrafe, g.gruppenId)}
|
||||
${renderSubSection('Zeitstrafen', sortByName(g.sperren || []), 'zeitstrafe', renderZeitstrafe, g.gruppenId)}
|
||||
${renderSubSection('Finisher', sortByName(g.finisher || []), 'finisher', renderFinisher, g.gruppenId)}
|
||||
${renderSubSection('Finisher', sortByGeschlecht(g.finisher || []), 'finisher', renderFinisher, g.gruppenId)}
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
@@ -1010,6 +1037,7 @@ function itemCard(id, kurzText, badges, rows, kind, gruppenId) {
|
||||
const actionBtns = `<div class="item-action-btns">
|
||||
<button class="btn-item-edit" onclick="openEditItemModal('${esc(id)}',event)">✎ Bearbeiten</button>
|
||||
<button class="btn-item-edit" onclick="duplicateItem('${kind}','${esc(id)}','${esc(gruppenId)}',event)">⧉ Duplizieren</button>
|
||||
<button class="btn-item-edit" onclick="openMoveModal('${kind}','${esc(id)}','${esc(gruppenId)}',event)">↪ Verschieben</button>
|
||||
<button class="btn-item-delete" onclick="deleteItem('${kind}','${esc(id)}','${esc(gruppenId)}',event)">✕ Löschen</button>
|
||||
</div>`;
|
||||
return `<div class="item" id="item-${esc(id)}">
|
||||
@@ -1073,6 +1101,52 @@ async function duplicateItem(kind, itemId, gruppenId, event) {
|
||||
else document.getElementById('gruppeActionError').textContent = 'Fehler beim Duplizieren (HTTP ' + r.status + ').';
|
||||
}
|
||||
|
||||
let _moveState = null;
|
||||
function openMoveModal(kind, itemId, currentGruppeId, event) {
|
||||
event.stopPropagation();
|
||||
const item = _itemData[itemId]; if (!item) return;
|
||||
_moveState = { kind, itemId, currentGruppeId };
|
||||
document.getElementById('moveModalItemName').textContent = item.kurzText || itemId;
|
||||
document.getElementById('moveModalError').textContent = '';
|
||||
const sel = document.getElementById('moveModalSelect');
|
||||
sel.innerHTML = '';
|
||||
Object.values(_gruppeData)
|
||||
.filter(g => g.gruppenId !== currentGruppeId)
|
||||
.sort((a, b) => (a.name || '').localeCompare(b.name || '', 'de'))
|
||||
.forEach(g => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = g.gruppenId;
|
||||
opt.textContent = g.name;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
document.getElementById('moveModal').classList.add('open');
|
||||
}
|
||||
function closeMoveModal() {
|
||||
document.getElementById('moveModal').classList.remove('open');
|
||||
_moveState = null;
|
||||
}
|
||||
document.getElementById('moveModalCancel').addEventListener('click', closeMoveModal);
|
||||
document.getElementById('moveModal').addEventListener('click', e => {
|
||||
if (e.target === document.getElementById('moveModal')) closeMoveModal();
|
||||
});
|
||||
document.getElementById('moveModalOk').addEventListener('click', async () => {
|
||||
if (!_moveState) return;
|
||||
const { kind, itemId, currentGruppeId } = _moveState;
|
||||
const targetGruppeId = document.getElementById('moveModalSelect').value;
|
||||
if (!targetGruppeId) return;
|
||||
const r = await fetch(`/admin/aufgabengruppen/items/${kind}/${itemId}/move?targetGruppeId=${targetGruppeId}`, {
|
||||
method: 'PUT'
|
||||
});
|
||||
if (r.ok || r.status === 204) {
|
||||
closeMoveModal();
|
||||
openItemId = null;
|
||||
pendingExpandId = currentGruppeId;
|
||||
loadAdminGruppen();
|
||||
} else {
|
||||
document.getElementById('moveModalError').textContent = 'Fehler beim Verschieben (HTTP ' + r.status + ').';
|
||||
}
|
||||
});
|
||||
|
||||
function sortByLevelThenName(items) {
|
||||
return items.slice().sort((a, b) => {
|
||||
const la = a.level ?? 999, lb = b.level ?? 999;
|
||||
@@ -1081,6 +1155,14 @@ function sortByLevelThenName(items) {
|
||||
});
|
||||
}
|
||||
function sortByName(items) { return items.slice().sort((a, b) => (a.kurzText || '').localeCompare(b.kurzText || '', 'de')); }
|
||||
const GESCHLECHT_ORDER = { WEIBLICH: 0, DIVERS: 1, MAENNLICH: 2 };
|
||||
function sortByGeschlecht(items) {
|
||||
return items.slice().sort((a, b) => {
|
||||
const ga = GESCHLECHT_ORDER[a.geschlecht] ?? 99, gb = GESCHLECHT_ORDER[b.geschlecht] ?? 99;
|
||||
if (ga !== gb) return ga - gb;
|
||||
return (a.kurzText || '').localeCompare(b.kurzText || '', 'de');
|
||||
});
|
||||
}
|
||||
function formatSek(von, bis) {
|
||||
if (von != null && bis != null) return `${von}–${bis} s`;
|
||||
if (von != null) return `ab ${von} s`; if (bis != null) return `bis ${bis} s`; return '';
|
||||
@@ -1308,8 +1390,116 @@ function openEditItemModal(itemId, event) {
|
||||
itemModal.classList.add('open'); document.getElementById('iKurzText').focus();
|
||||
}
|
||||
|
||||
function closeItemModal() { itemModal.classList.remove('open'); closeToySearch(); document.getElementById('iPlaceholderHint').style.display = 'none'; }
|
||||
function closeItemModal() { itemModal.classList.remove('open'); closeToySearch(); document.getElementById('iPlaceholderHint').style.display = 'none'; document.getElementById('iTextAC').style.display = 'none'; }
|
||||
function togglePlaceholderHint() { const el = document.getElementById('iPlaceholderHint'); el.style.display = el.style.display === 'none' ? 'block' : 'none'; }
|
||||
|
||||
(function() {
|
||||
const STATIC = ['{AKTIV}', '{PASSIV}'];
|
||||
const ta = document.getElementById('iText');
|
||||
const ac = document.getElementById('iTextAC');
|
||||
let _allToys = [];
|
||||
let _items = [];
|
||||
let _wordStart = 0;
|
||||
let activeIdx = -1;
|
||||
|
||||
function currentWord() {
|
||||
const pos = ta.selectionStart;
|
||||
let start = pos;
|
||||
while (start > 0 && !/\s/.test(ta.value[start - 1])) start--;
|
||||
return { word: ta.value.slice(start, pos).toLowerCase(), start };
|
||||
}
|
||||
function buildItems(filter) {
|
||||
const f = filter || '';
|
||||
_items = STATIC.filter(s => !f || s.toLowerCase().includes(f)).map(s => ({ label: s, insert: s }));
|
||||
const toys = _allToys.filter(t => !f || t.name.toLowerCase().includes(f));
|
||||
if (toys.length) {
|
||||
_items.push({ separator: true, label: 'Toys' });
|
||||
toys.forEach(t => _items.push({ label: t.name, insert: t.name, toyId: t.toyId }));
|
||||
}
|
||||
}
|
||||
function selectables() { return _items.map((it, i) => it.separator ? null : i).filter(i => i !== null); }
|
||||
function renderItems() {
|
||||
ac.innerHTML = '';
|
||||
activeIdx = -1;
|
||||
_items.forEach((item, i) => {
|
||||
if (item.separator) {
|
||||
const sep = document.createElement('div');
|
||||
sep.className = 'ac-separator';
|
||||
sep.textContent = item.label;
|
||||
ac.appendChild(sep);
|
||||
} else {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'ac-item';
|
||||
div.dataset.idx = String(i);
|
||||
div.textContent = item.label;
|
||||
div.addEventListener('mousedown', e => { e.preventDefault(); doInsert(item); });
|
||||
div.addEventListener('mouseover', () => setActive(i));
|
||||
ac.appendChild(div);
|
||||
}
|
||||
});
|
||||
const s = selectables();
|
||||
if (s.length) { setActive(s[0]); ac.style.display = 'block'; } else hideAC();
|
||||
}
|
||||
function showAC(toys) {
|
||||
_allToys = toys || [];
|
||||
const { word, start } = currentWord();
|
||||
_wordStart = start;
|
||||
buildItems(word);
|
||||
const rect = ta.getBoundingClientRect();
|
||||
ac.style.left = rect.left + 'px';
|
||||
ac.style.top = (rect.bottom + 4) + 'px';
|
||||
renderItems();
|
||||
}
|
||||
function hideAC() { ac.style.display = 'none'; activeIdx = -1; }
|
||||
function setActive(i) {
|
||||
activeIdx = i;
|
||||
let activeEl = null;
|
||||
ac.querySelectorAll('.ac-item').forEach(el => {
|
||||
const on = parseInt(el.dataset.idx) === i;
|
||||
el.classList.toggle('ac-item-active', on);
|
||||
if (on) activeEl = el;
|
||||
});
|
||||
if (activeEl) activeEl.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
function doInsert(item) {
|
||||
const end = ta.selectionStart;
|
||||
ta.value = ta.value.slice(0, _wordStart) + item.insert + ta.value.slice(end);
|
||||
ta.selectionStart = ta.selectionEnd = _wordStart + item.insert.length;
|
||||
ta.focus();
|
||||
if (item.toyId) {
|
||||
const already = (_selectedToys || []).find(t => t.toyId === item.toyId);
|
||||
if (!already) toggleToyFromSearch(item.toyId);
|
||||
}
|
||||
hideAC();
|
||||
}
|
||||
ta.addEventListener('keydown', e => {
|
||||
if (e.ctrlKey && e.code === 'Space') {
|
||||
e.preventDefault();
|
||||
_loadAvailableToys().then(toys => showAC(toys)).catch(() => showAC([]));
|
||||
return;
|
||||
}
|
||||
if (ac.style.display !== 'block') return;
|
||||
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); hideAC(); return; }
|
||||
const s = selectables();
|
||||
if (!s.length) return;
|
||||
const pos = s.indexOf(activeIdx);
|
||||
if (e.key === 'ArrowDown') { e.preventDefault(); setActive(s[(pos + 1) % s.length]); }
|
||||
else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(s[(pos - 1 + s.length) % s.length]); }
|
||||
else if (e.key === 'Enter' || e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const item = _items[activeIdx];
|
||||
if (item && !item.separator) doInsert(item);
|
||||
}
|
||||
});
|
||||
ta.addEventListener('input', () => {
|
||||
if (ac.style.display !== 'block') return;
|
||||
const { word, start } = currentWord();
|
||||
_wordStart = start;
|
||||
buildItems(word);
|
||||
renderItems();
|
||||
});
|
||||
document.addEventListener('mousedown', e => { if (!ac.contains(e.target) && e.target !== ta) hideAC(); });
|
||||
})();
|
||||
document.getElementById('itemCancelBtn').addEventListener('click', closeItemModal);
|
||||
itemModal.addEventListener('click', e => { if (e.target === itemModal) closeItemModal(); });
|
||||
|
||||
|
||||
@@ -883,7 +883,8 @@ body.app {
|
||||
}
|
||||
|
||||
/* Benachrichtigungen */
|
||||
.topbar-notif-item--unread { background: rgba(var(--color-primary-rgb, 231,57,84), 0.04); }
|
||||
.topbar-notif-item--unread { background: rgba(var(--color-primary-rgb, 231,57,84), 0.07); border-left: 3px solid var(--color-primary); }
|
||||
.topbar-notif-item--unread:hover { background: rgba(var(--color-primary-rgb, 231,57,84), 0.12); }
|
||||
.topbar-notif-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0;url=/games/bdsm/neubdsm.html">
|
||||
<title>BDSM Game – xXx Sphere</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>window.location.replace('/games/bdsm/neubdsm.html');</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0;url=/games/bdsm/neubdsm.html">
|
||||
<title>BDSM Game – xXx Sphere</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>window.location.replace('/games/bdsm/neubdsm.html');</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0;url=/games/bdsm/neubdsm.html">
|
||||
<title>BDSM Game – xXx Sphere</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>window.location.replace('/games/bdsm/neubdsm.html');</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0;url=/games/bdsm/neubdsm.html">
|
||||
<title>BDSM Game – xXx Sphere</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>window.location.replace('/games/bdsm/neubdsm.html');</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div style="font-size:2.5rem;margin-bottom:1rem;">⚠️</div>
|
||||
<h2 style="margin-bottom:0.5rem;">Einladung nicht gefunden</h2>
|
||||
<p style="color:var(--color-muted);">Diese Einladung existiert nicht oder wurde bereits bearbeitet.</p>
|
||||
<a href="/community/einladungen.html" style="display:inline-block;margin-top:1.5rem;padding:0.65rem 1.5rem;background:var(--color-primary);color:#fff;border-radius:8px;text-decoration:none;font-weight:600;">Zu meinen Einladungen</a>
|
||||
<a href="/games/common/einladungen.html" style="display:inline-block;margin-top:1.5rem;padding:0.65rem 1.5rem;background:var(--color-primary);color:#fff;border-radius:8px;text-decoration:none;font-weight:600;">Zu meinen Einladungen</a>
|
||||
</div>
|
||||
|
||||
<div id="stateAlready" style="text-align:center;padding:3rem 1rem;">
|
||||
@@ -117,7 +117,7 @@
|
||||
<div style="font-size:2.5rem;margin-bottom:1rem;">✕</div>
|
||||
<h2 style="margin-bottom:0.5rem;">Einladung abgelehnt</h2>
|
||||
<p style="color:var(--color-muted);">Du hast die Einladung abgelehnt. Der Keyholder wurde benachrichtigt.</p>
|
||||
<a href="/community/einladungen.html" style="display:inline-block;margin-top:1.5rem;padding:0.65rem 1.5rem;background:var(--color-primary);color:#fff;border-radius:8px;text-decoration:none;font-weight:600;">Zu meinen Einladungen</a>
|
||||
<a href="/games/common/einladungen.html" style="display:inline-block;margin-top:1.5rem;padding:0.65rem 1.5rem;background:var(--color-primary);color:#fff;border-radius:8px;text-decoration:none;font-weight:600;">Zu meinen Einladungen</a>
|
||||
</div>
|
||||
|
||||
<div id="stateInvite" style="display:none;">
|
||||
|
||||
@@ -839,7 +839,7 @@
|
||||
|
||||
const data = await res.json();
|
||||
if (data.lockeeInvitationSent) {
|
||||
window.location.href = '/community/einladungen.html?tab=gesendet';
|
||||
window.location.href = '/games/common/einladungen.html?tab=gesendet';
|
||||
} else if (!data.unlockCode) {
|
||||
// Trust: kein Code, direkt weiter
|
||||
const isTimeLock = selectedTemplate && selectedTemplate._type === 'timelock';
|
||||
|
||||
@@ -227,6 +227,15 @@
|
||||
background: rgba(233,69,96,0.12); color: var(--color-primary);
|
||||
border-radius: 3px; padding: 0.05rem 0.3rem; font-size: 0.75rem;
|
||||
}
|
||||
#iTextAC { position: fixed; z-index: 9999; background: var(--color-surface, #1e1e2e);
|
||||
border: 1px solid var(--color-border, #444); border-radius: 6px;
|
||||
box-shadow: 0 4px 14px rgba(0,0,0,.5); display: none; overflow: hidden; min-width: 180px; max-height: 280px; overflow-y: auto; }
|
||||
.ac-item { padding: 0.45rem 0.9rem; cursor: pointer; font-size: 0.88rem;
|
||||
font-family: monospace; color: var(--color-text, #cdd6f4); user-select: none; }
|
||||
.ac-item:hover, .ac-item-active { background: var(--color-primary, #cba6f7); color: #1e1e2e; }
|
||||
.ac-separator { padding: 0.2rem 0.7rem; font-size: 0.72rem; color: var(--color-muted);
|
||||
text-transform: uppercase; letter-spacing: 0.05em; background: rgba(255,255,255,0.04);
|
||||
pointer-events: none; border-top: 1px solid rgba(136,136,136,0.2); margin-top: 2px; }
|
||||
|
||||
/* ── Item-Add-Modal extra ── */
|
||||
.modal-two-col { display: flex; gap: 0.75rem; }
|
||||
@@ -407,6 +416,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="iText" rows="4" maxlength="4000" placeholder="Ausführliche Beschreibung…"></textarea>
|
||||
<div id="iTextAC"></div>
|
||||
|
||||
<!-- Finisher: Geschlecht -->
|
||||
<div id="iGeschlechtRow">
|
||||
@@ -734,7 +744,7 @@
|
||||
${renderSubSection('Aufgaben', sortByLevelThenName(g.aufgaben || []), 'aufgabe', renderAufgabe, g.gruppenId, type)}
|
||||
${IS_BDSM_MODE ? renderSubSection('Strafen', sortByLevelThenName(g.strafen || []), 'strafe', renderStrafe, g.gruppenId, type) : ''}
|
||||
${IS_BDSM_MODE ? renderSubSection('Zeitstrafen',sortByName(g.sperren || []), 'zeitstrafe',renderZeitstrafe, g.gruppenId, type) : ''}
|
||||
${renderSubSection('Finisher', sortByName(g.finisher || []), 'finisher', renderFinisher, g.gruppenId, type)}
|
||||
${renderSubSection('Finisher', sortByGeschlecht(g.finisher || []), 'finisher', renderFinisher, g.gruppenId, type)}
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
@@ -945,6 +955,14 @@
|
||||
return (a.kurzText || '').localeCompare(b.kurzText || '', 'de');
|
||||
});
|
||||
}
|
||||
const GESCHLECHT_ORDER = { WEIBLICH: 0, DIVERS: 1, MAENNLICH: 2 };
|
||||
function sortByGeschlecht(items) {
|
||||
return items.slice().sort((a, b) => {
|
||||
const ga = GESCHLECHT_ORDER[a.geschlecht] ?? 99, gb = GESCHLECHT_ORDER[b.geschlecht] ?? 99;
|
||||
if (ga !== gb) return ga - gb;
|
||||
return (a.kurzText || '').localeCompare(b.kurzText || '', 'de');
|
||||
});
|
||||
}
|
||||
function sortByName(items) {
|
||||
return items.slice().sort((a, b) => (a.kurzText || '').localeCompare(b.kurzText || '', 'de'));
|
||||
}
|
||||
@@ -1429,12 +1447,120 @@
|
||||
document.getElementById('iKurzText').focus();
|
||||
}
|
||||
|
||||
function closeItemModal() { itemModal.classList.remove('open'); closeToySearch(); document.getElementById('iPlaceholderHint').style.display = 'none'; }
|
||||
function closeItemModal() { itemModal.classList.remove('open'); closeToySearch(); document.getElementById('iPlaceholderHint').style.display = 'none'; document.getElementById('iTextAC').style.display = 'none'; }
|
||||
function togglePlaceholderHint() {
|
||||
const el = document.getElementById('iPlaceholderHint');
|
||||
el.style.display = el.style.display === 'none' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
(function() {
|
||||
const STATIC = ['{AKTIV}', '{PASSIV}'];
|
||||
const ta = document.getElementById('iText');
|
||||
const ac = document.getElementById('iTextAC');
|
||||
let _allToys = [];
|
||||
let _items = [];
|
||||
let _wordStart = 0;
|
||||
let activeIdx = -1;
|
||||
|
||||
function currentWord() {
|
||||
const pos = ta.selectionStart;
|
||||
let start = pos;
|
||||
while (start > 0 && !/\s/.test(ta.value[start - 1])) start--;
|
||||
return { word: ta.value.slice(start, pos).toLowerCase(), start };
|
||||
}
|
||||
function buildItems(filter) {
|
||||
const f = filter || '';
|
||||
_items = STATIC.filter(s => !f || s.toLowerCase().includes(f)).map(s => ({ label: s, insert: s }));
|
||||
const toys = _allToys.filter(t => !f || t.name.toLowerCase().includes(f));
|
||||
if (toys.length) {
|
||||
_items.push({ separator: true, label: 'Toys' });
|
||||
toys.forEach(t => _items.push({ label: t.name, insert: t.name, toyId: t.toyId }));
|
||||
}
|
||||
}
|
||||
function selectables() { return _items.map((it, i) => it.separator ? null : i).filter(i => i !== null); }
|
||||
function renderItems() {
|
||||
ac.innerHTML = '';
|
||||
activeIdx = -1;
|
||||
_items.forEach((item, i) => {
|
||||
if (item.separator) {
|
||||
const sep = document.createElement('div');
|
||||
sep.className = 'ac-separator';
|
||||
sep.textContent = item.label;
|
||||
ac.appendChild(sep);
|
||||
} else {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'ac-item';
|
||||
div.dataset.idx = String(i);
|
||||
div.textContent = item.label;
|
||||
div.addEventListener('mousedown', e => { e.preventDefault(); doInsert(item); });
|
||||
div.addEventListener('mouseover', () => setActive(i));
|
||||
ac.appendChild(div);
|
||||
}
|
||||
});
|
||||
const s = selectables();
|
||||
if (s.length) { setActive(s[0]); ac.style.display = 'block'; } else hideAC();
|
||||
}
|
||||
function showAC(toys) {
|
||||
_allToys = toys || [];
|
||||
const { word, start } = currentWord();
|
||||
_wordStart = start;
|
||||
buildItems(word);
|
||||
const rect = ta.getBoundingClientRect();
|
||||
ac.style.left = rect.left + 'px';
|
||||
ac.style.top = (rect.bottom + 4) + 'px';
|
||||
renderItems();
|
||||
}
|
||||
function hideAC() { ac.style.display = 'none'; activeIdx = -1; }
|
||||
function setActive(i) {
|
||||
activeIdx = i;
|
||||
let activeEl = null;
|
||||
ac.querySelectorAll('.ac-item').forEach(el => {
|
||||
const on = parseInt(el.dataset.idx) === i;
|
||||
el.classList.toggle('ac-item-active', on);
|
||||
if (on) activeEl = el;
|
||||
});
|
||||
if (activeEl) activeEl.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
function doInsert(item) {
|
||||
const end = ta.selectionStart;
|
||||
ta.value = ta.value.slice(0, _wordStart) + item.insert + ta.value.slice(end);
|
||||
ta.selectionStart = ta.selectionEnd = _wordStart + item.insert.length;
|
||||
ta.focus();
|
||||
if (item.toyId) {
|
||||
const already = (_selectedToys || []).find(t => t.toyId === item.toyId);
|
||||
if (!already) toggleToyFromSearch(item.toyId);
|
||||
}
|
||||
hideAC();
|
||||
}
|
||||
ta.addEventListener('keydown', e => {
|
||||
if (e.ctrlKey && e.code === 'Space') {
|
||||
e.preventDefault();
|
||||
_loadAvailableToys().then(toys => showAC(toys)).catch(() => showAC([]));
|
||||
return;
|
||||
}
|
||||
if (ac.style.display !== 'block') return;
|
||||
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); hideAC(); return; }
|
||||
const s = selectables();
|
||||
if (!s.length) return;
|
||||
const pos = s.indexOf(activeIdx);
|
||||
if (e.key === 'ArrowDown') { e.preventDefault(); setActive(s[(pos + 1) % s.length]); }
|
||||
else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(s[(pos - 1 + s.length) % s.length]); }
|
||||
else if (e.key === 'Enter' || e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const item = _items[activeIdx];
|
||||
if (item && !item.separator) doInsert(item);
|
||||
}
|
||||
});
|
||||
ta.addEventListener('input', () => {
|
||||
if (ac.style.display !== 'block') return;
|
||||
const { word, start } = currentWord();
|
||||
_wordStart = start;
|
||||
buildItems(word);
|
||||
renderItems();
|
||||
});
|
||||
document.addEventListener('mousedown', e => { if (!ac.contains(e.target) && e.target !== ta) hideAC(); });
|
||||
})();
|
||||
|
||||
document.getElementById('itemCancelBtn').addEventListener('click', closeItemModal);
|
||||
itemModal.addEventListener('click', e => { if (e.target === itemModal) closeItemModal(); });
|
||||
|
||||
@@ -602,6 +602,18 @@
|
||||
document.getElementById('confirmTitle').textContent = title;
|
||||
document.getElementById('confirmText').textContent = text;
|
||||
document.getElementById('confirmModal').classList.add('open');
|
||||
document.querySelector('#confirmModal .confirm-modal-cancel').style.display = '';
|
||||
return new Promise(resolve => {
|
||||
_confirmResolve = resolve;
|
||||
document.getElementById('confirmOkBtn').onclick = () => { confirmClose(true); };
|
||||
});
|
||||
}
|
||||
|
||||
function showInfo(title, text) {
|
||||
document.getElementById('confirmTitle').textContent = title;
|
||||
document.getElementById('confirmText').textContent = text;
|
||||
document.getElementById('confirmModal').classList.add('open');
|
||||
document.querySelector('#confirmModal .confirm-modal-cancel').style.display = 'none';
|
||||
return new Promise(resolve => {
|
||||
_confirmResolve = resolve;
|
||||
document.getElementById('confirmOkBtn').onclick = () => { confirmClose(true); };
|
||||
@@ -885,6 +897,9 @@
|
||||
removeRecvItem(key);
|
||||
if (mode === 'OWN_DEVICE') {
|
||||
window.location.href = `/games/bdsm/neubdsm.html`;
|
||||
} else if (mode === 'HOST_DEVICE') {
|
||||
await showInfo('Einladung angenommen', 'Das Spiel findet am Gerät des Hosts statt. Du wirst zur Startseite weitergeleitet.');
|
||||
window.location.href = '/userhome.html';
|
||||
}
|
||||
} catch (_) {
|
||||
errEl.textContent = 'Fehler beim Speichern der Antwort.';
|
||||
@@ -933,6 +948,7 @@
|
||||
if (mode === 'OWN_DEVICE') {
|
||||
window.location.href = '/games/vanilla/neuvanilla.html';
|
||||
} else if (mode === 'HOST_DEVICE') {
|
||||
await showInfo('Einladung angenommen', 'Das Spiel findet am Gerät des Hosts statt. Du wirst zur Startseite weitergeleitet.');
|
||||
window.location.href = '/userhome.html';
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -190,7 +190,7 @@
|
||||
<div class="acc-body" id="acc-aufgaben-body">
|
||||
<div id="guestAufgabenHint" class="guest-hint" style="display:none;">Aufgaben werden vom Host festgelegt – nur zur Ansicht.</div>
|
||||
<p style="font-size:0.85rem;color:var(--color-muted);margin-bottom:0.75rem;">
|
||||
Gruppen verwalten: <a href="/games/bdsm/aufgaben.html" style="color:var(--color-primary);">Aufgaben-Verwaltung (Vanilla)</a>
|
||||
Gruppen verwalten: <a href="/games/common/aufgaben.html" style="color:var(--color-primary);">Aufgaben-Verwaltung (Vanilla)</a>
|
||||
</p>
|
||||
<div id="sectionOwn">
|
||||
<div class="aufgaben-section-label"><label class="select-all-label"><input type="checkbox" class="select-all-cb" data-list="listOwn"> Eigene Gruppen</label></div>
|
||||
@@ -566,11 +566,14 @@
|
||||
const ul = document.getElementById(containerId);
|
||||
const section = ul.closest('[id^="section"]');
|
||||
const selectAllWrap = section?.querySelector('.select-all-label');
|
||||
if (!gruppen.length) {
|
||||
const filtered = gruppen.filter(g =>
|
||||
(g.aufgaben || []).length > 0 || (g.finisher || []).length > 0
|
||||
);
|
||||
if (!filtered.length) {
|
||||
ul.innerHTML = '<li class="empty-hint">Keine Gruppen vorhanden.</li>';
|
||||
if (selectAllWrap) selectAllWrap.style.visibility = 'hidden'; return;
|
||||
}
|
||||
ul.innerHTML = gruppen.map(g => {
|
||||
ul.innerHTML = filtered.map(g => {
|
||||
const checked = savedGruppen.has(g.gruppenId);
|
||||
return `<li><label class="gruppe-item${checked ? ' is-checked' : ''}">
|
||||
<input type="checkbox" value="${g.gruppenId}"${checked ? ' checked' : ''}>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0;url=/games/vanilla/neuvanilla.html">
|
||||
<title>Vanilla Game – xXx Sphere</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>window.location.replace('/games/vanilla/neuvanilla.html');</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,8 +10,8 @@
|
||||
{ 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/bdsm/aufgaben.html', icon: I('CHECK'), label: 'Aufgaben' },
|
||||
{ href: '/games/chastity/toys.html', icon: I('TOYS'), label: 'Toys' },
|
||||
{ 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' },
|
||||
]
|
||||
},
|
||||
@@ -22,8 +22,8 @@
|
||||
{ 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/bdsm/aufgaben.html?mode=bdsm', icon: I('CHECK'), label: 'Aufgaben' },
|
||||
{ href: '/games/chastity/toys.html', icon: I('TOYS'), label: 'Toys' },
|
||||
{ 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' },
|
||||
]
|
||||
},
|
||||
@@ -56,7 +56,7 @@
|
||||
{ href: '/community/nachrichten.html', icon: I('MESSAGES'), label: 'Nachrichten', badgeId: null },
|
||||
{ href: '/community/benachrichtigungen.html', icon: I('NOTIFICATIONS'), label: 'Benachrichtigungen', badgeId: null },
|
||||
{ href: '/community/gruppen.html', icon: I('GROUPS'), label: 'Gruppen', badgeId: 'socialGruppenBadge'},
|
||||
{ href: '/community/einladungen.html', icon: I('INVITATIONS'), label: 'Einladungen', badgeId: null },
|
||||
{ href: '/games/common/einladungen.html', icon: I('INVITATIONS'), label: 'Einladungen', badgeId: null },
|
||||
];
|
||||
const socialNav = socialLinks.map(({ href, icon, label, badgeId }) => {
|
||||
const cls = path === href ? ' class="active"' : '';
|
||||
|
||||
@@ -85,6 +85,12 @@
|
||||
} catch(ex) {}
|
||||
});
|
||||
|
||||
es.addEventListener('INVITATION', () => {
|
||||
try {
|
||||
if (typeof window.__topbarReloadInvBadge === 'function') window.__topbarReloadInvBadge();
|
||||
} catch(ex) {}
|
||||
});
|
||||
|
||||
es.onerror = () => {
|
||||
es.close();
|
||||
// Vor dem Reconnect prüfen ob noch eingeloggt (verhindert Endlos-Schleife bei abgelaufener Session)
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<button class="topbar-panel-close" onclick="window.__topbarCloseAll()">✕</button>
|
||||
</div>
|
||||
<div class="topbar-panel-body" id="topbarInvBody"></div>
|
||||
<div class="topbar-panel-footer"><a href="/community/einladungen.html">Alle anzeigen →</a></div>
|
||||
<div class="topbar-panel-footer"><a href="/games/common/einladungen.html">Alle anzeigen →</a></div>
|
||||
</div>
|
||||
|
||||
<div class="topbar-panel" id="topbarProfilePanel">
|
||||
@@ -275,17 +275,17 @@
|
||||
try {
|
||||
const res = await fetch('/notifications');
|
||||
if (!res.ok) { body.innerHTML = '<div class="topbar-panel-hint">Keine Benachrichtigungen.</div>'; return; }
|
||||
const notifs = await res.json();
|
||||
if (!notifs.length) { body.innerHTML = '<div class="topbar-panel-hint">Keine neuen Benachrichtigungen.</div>'; return; }
|
||||
const unread = (await res.json()).filter(n => !n.read);
|
||||
if (!unread.length) { body.innerHTML = '<div class="topbar-panel-hint">Keine neuen Benachrichtigungen.</div>'; return; }
|
||||
body.innerHTML = '';
|
||||
notifs.forEach(n => {
|
||||
unread.forEach(n => {
|
||||
const el = document.createElement('div');
|
||||
const tag = n.targetUrl ? 'a' : 'div';
|
||||
const href = n.targetUrl ? `href="${esc(n.targetUrl)}"` : '';
|
||||
const av = n.senderAvatar
|
||||
? `<img src="data:image/png;base64,${esc(n.senderAvatar)}" class="topbar-item-avatar" alt="">`
|
||||
: `<span class="topbar-item-avatar topbar-item-avatar--placeholder">${IC('PROFILE')}</span>`;
|
||||
el.innerHTML = `<${tag} ${href} class="topbar-panel-item topbar-notif-item">
|
||||
el.innerHTML = `<${tag} ${href} class="topbar-panel-item topbar-notif-item${n.read ? '' : ' topbar-notif-item--unread'}">
|
||||
${av}
|
||||
<div class="topbar-panel-item-body">
|
||||
<div style="font-size:0.85rem;line-height:1.4;">${esc(n.text)}</div>
|
||||
@@ -323,18 +323,21 @@
|
||||
if (!body) return;
|
||||
body.innerHTML = '<div class="topbar-panel-hint">Wird geladen…</div>';
|
||||
try {
|
||||
const [lr, kr, br] = await Promise.all([
|
||||
const [lr, kr, br, vr] = await Promise.all([
|
||||
fetch('/lockee/invitations/mine'),
|
||||
fetch('/keyholder/invitations/mine'),
|
||||
fetch('/bdsm/einladung/pending')
|
||||
fetch('/bdsm/einladung/pending'),
|
||||
fetch('/vanilla/einladung/pending')
|
||||
]);
|
||||
const lockee = lr.ok ? await lr.json() : [];
|
||||
const kh = kr.ok ? await kr.json() : [];
|
||||
const bdsm = br.ok ? await br.json() : [];
|
||||
const lockee = lr.ok ? await lr.json() : [];
|
||||
const kh = kr.ok ? await kr.json() : [];
|
||||
const bdsm = br.ok ? await br.json() : [];
|
||||
const vanilla = vr.ok ? await vr.json() : [];
|
||||
const all = [
|
||||
...lockee.map(i => ({ ...i, _type: 'lockee' })),
|
||||
...kh.map(i => ({ ...i, _type: 'keyholder' })),
|
||||
...bdsm.map(i => ({ ...i, _type: 'bdsm' }))
|
||||
...bdsm.map(i => ({ ...i, _type: 'bdsm' })),
|
||||
...vanilla.map(i => ({ ...i, _type: 'vanilla' }))
|
||||
];
|
||||
if (!all.length) { body.innerHTML = '<div class="topbar-panel-hint">Keine offenen Einladungen.</div>'; return; }
|
||||
body.innerHTML = '';
|
||||
@@ -343,66 +346,35 @@
|
||||
}
|
||||
|
||||
function buildInvCard(inv) {
|
||||
let typeIcon, typeName, line, declineUrl, declineMethod = 'DELETE', declineBody = null, acceptHtml;
|
||||
let typeIcon, typeName, line;
|
||||
|
||||
if (inv._type === 'lockee') {
|
||||
typeIcon = IC('LOCK'); typeName = 'Lockee-Einladung'; line = inv.lockName || 'Lock';
|
||||
declineUrl = '/lockee/invitation/' + encodeURIComponent(inv.token);
|
||||
acceptHtml = `<a href="/community/einladungen.html" class="topbar-inv-btn topbar-inv-btn--accept">Details</a>`;
|
||||
} else if (inv._type === 'keyholder') {
|
||||
typeIcon = IC('KEY'); typeName = 'Keyholder-Einladung'; line = inv.lockName || 'Lock';
|
||||
declineUrl = '/keyholder/invitations/mine/' + encodeURIComponent(inv.token);
|
||||
acceptHtml = `<a href="/keyholder/invitation/${esc(inv.token)}" class="topbar-inv-btn topbar-inv-btn--accept">Annehmen</a>`;
|
||||
} else if (inv._type === 'vanilla') {
|
||||
typeIcon = IC('INVITATIONS'); typeName = 'Vanilla Game'; line = inv.inviterName || 'Einladung';
|
||||
} else {
|
||||
typeIcon = IC('BDSM'); typeName = 'BDSM Game'; line = inv.senderName || 'Einladung';
|
||||
const id = inv.id || inv.einladungId || '';
|
||||
declineUrl = '/bdsm/einladung/' + encodeURIComponent(id) + '/antwort';
|
||||
declineMethod = 'PUT';
|
||||
declineBody = JSON.stringify({ annahme: false });
|
||||
acceptHtml = `<a href="/games/bdsm/bdsm-einladung.html?id=${esc(id)}" class="topbar-inv-btn topbar-inv-btn--accept">Details</a>`;
|
||||
}
|
||||
|
||||
const senderPic = inv.senderAvatar || inv.lockOwnerAvatar;
|
||||
const senderPic = inv.senderAvatar || inv.lockOwnerAvatar || inv.inviterAvatar;
|
||||
const av = senderPic
|
||||
? `<img src="data:image/png;base64,${esc(senderPic)}" class="topbar-item-avatar" alt="">`
|
||||
: `<span class="topbar-item-avatar topbar-item-avatar--placeholder">${IC('PROFILE')}</span>`;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.className = 'topbar-panel-item topbar-inv-card';
|
||||
div.style.cursor = 'pointer';
|
||||
div.innerHTML = `${av}
|
||||
<div class="topbar-panel-item-body">
|
||||
<div class="topbar-panel-item-sub">${typeIcon} ${typeName}</div>
|
||||
<div style="font-weight:600;font-size:0.88rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${esc(line)}</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:0.3rem;flex-shrink:0;">
|
||||
<button class="topbar-inv-btn topbar-inv-btn--decline"
|
||||
data-url="${esc(declineUrl)}"
|
||||
data-method="${declineMethod}"
|
||||
data-body="${esc(declineBody || '')}"
|
||||
onclick="window.__topbarDecline(this)">✕</button>
|
||||
${acceptHtml}
|
||||
</div>`;
|
||||
div.addEventListener('click', () => { window.location.href = '/games/common/einladungen.html'; });
|
||||
return div;
|
||||
}
|
||||
|
||||
window.__topbarDecline = async function (btn) {
|
||||
btn.disabled = true;
|
||||
const url = btn.dataset.url;
|
||||
const method = btn.dataset.method;
|
||||
const body = btn.dataset.body || null;
|
||||
try {
|
||||
const opts = { method };
|
||||
if (body) { opts.headers = { 'Content-Type': 'application/json' }; opts.body = body; }
|
||||
const res = await fetch(url, opts);
|
||||
if (res.ok || res.status === 204) {
|
||||
const card = btn.closest('.topbar-inv-card');
|
||||
if (card) card.remove();
|
||||
const remaining = document.getElementById('topbarInvBody')?.querySelectorAll('.topbar-inv-card').length || 0;
|
||||
setTopbarBadge('inv', remaining);
|
||||
} else { btn.disabled = false; }
|
||||
} catch (e) { btn.disabled = false; }
|
||||
};
|
||||
|
||||
// ── Badge-Verwaltung ──
|
||||
function setTopbarBadge(type, count) {
|
||||
const map = { msg: 'topbarMsgBadge', notif: 'topbarNotifBadge', inv: 'topbarInvBadge' };
|
||||
@@ -415,9 +387,7 @@
|
||||
// Für social-sidebar.js zugänglich
|
||||
window.__topbarSetBadge = setTopbarBadge;
|
||||
|
||||
function loadInitialBadges() {
|
||||
fetch('/social/messages/unread/count').then(r => r.ok ? r.json() : 0).then(n => setTopbarBadge('msg', n)).catch(() => {});
|
||||
fetch('/notifications/unread/count').then(r => r.ok ? r.json() : 0).then(n => setTopbarBadge('notif', n)).catch(() => {});
|
||||
function reloadInvBadge() {
|
||||
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),
|
||||
@@ -425,4 +395,11 @@
|
||||
fetch('/vanilla/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0)
|
||||
]).then(([l, k, b, v]) => setTopbarBadge('inv', l + k + b + v)).catch(() => {});
|
||||
}
|
||||
window.__topbarReloadInvBadge = reloadInvBadge;
|
||||
|
||||
function loadInitialBadges() {
|
||||
fetch('/social/messages/unread/count').then(r => r.ok ? r.json() : 0).then(n => setTopbarBadge('msg', n)).catch(() => {});
|
||||
fetch('/notifications/unread/count').then(r => r.ok ? r.json() : 0).then(n => setTopbarBadge('notif', n)).catch(() => {});
|
||||
reloadInvBadge();
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user