Improve Challenge-Logik... aber noch nicht fertig

This commit is contained in:
DST81 2026-05-25 13:25:34 +02:00
parent c060590a4a
commit be451f36ce
5 changed files with 273 additions and 35 deletions

View File

@ -135,9 +135,9 @@
<script src="assets/src/service/challenge-service.js"></script>
<script src="js/login.js"></script>
<script src="js/leaderboard.js"></script>
<script src="js/messages.js"></script>
<script src="js/messages.js?v=challenge-flow-20260525"></script>
<!--Navigation Script -->
<script src="js/play.js"></script>
<script src="js/navigation.js"></script>
<script src="js/play.js?v=challenge-flow-20260525"></script>
<script src="js/navigation.js?v=challenge-flow-20260525"></script>
</body>
</html>

View File

@ -3,6 +3,7 @@
const MESSAGE_TYPE_CHALLENGE_RESULT = "challenge-result";
const MESSAGE_POLL_INTERVAL_MS = 30000;
const ACTIVE_CHALLENGE_STORAGE_KEY = "loremIpsumActiveChallenge";
const CHALLENGE_DATA_PREFIX = "[[loremIpsumChallenge:";
let currentMessages = [];
let currentUsers = [];
@ -50,18 +51,159 @@
}
function normalizeMessage(message) {
const embeddedChallenge = extractEmbeddedChallenge(message.text ?? "");
const type = message.type ?? MESSAGE_TYPE_CHALLENGE;
const challenge = message.challenge
?? message.result
?? embeddedChallenge.challenge
?? (type === MESSAGE_TYPE_CHALLENGE || type === MESSAGE_TYPE_CHALLENGE_RESULT ? message : null);
return {
id: message.id,
sender: message.sender ?? message.from ?? "",
recipient: message.recipient ?? message.to ?? "",
type: message.type ?? MESSAGE_TYPE_CHALLENGE,
text: message.text ?? "",
type: type,
text: embeddedChallenge.text,
read: Boolean(message.read),
createdAt: message.createdAt ?? message.time ?? message.date ?? "",
challenge: message.challenge ?? message.result ?? null,
challenge: challenge,
};
}
function extractEmbeddedChallenge(text) {
const rawText = String(text ?? "");
if (!rawText.startsWith(CHALLENGE_DATA_PREFIX)) {
return {
text: rawText,
challenge: null,
};
}
const endIndex = rawText.indexOf("]]");
if (endIndex === -1) {
return {
text: rawText,
challenge: null,
};
}
const json = rawText.slice(CHALLENGE_DATA_PREFIX.length, endIndex);
const displayText = rawText.slice(endIndex + 2).trim();
try {
return {
text: displayText,
challenge: JSON.parse(json),
};
} catch {
return {
text: rawText,
challenge: null,
};
}
}
function getChallengeId(challenge) {
return challenge?.challengeId ?? challenge?.challenge_id ?? challenge?.id ?? null;
}
function getChallengeChallenger(challenge, fallbackName) {
return challenge?.challenger
?? challenge?.challengerName
?? challenge?.sender
?? challenge?.from
?? fallbackName
?? "";
}
function getChallengeOpponent(challenge, fallbackName) {
return challenge?.opponent
?? challenge?.opponentName
?? challenge?.challengedUser
?? challenge?.recipient
?? challenge?.to
?? fallbackName
?? "";
}
function getOpponentScore(challenge) {
return challenge?.opponentScore
?? challenge?.challengedScore
?? challenge?.challengedUserScore
?? null;
}
function hasScore(value) {
return value !== null && value !== undefined && value !== "";
}
function getChallengeRole(message) {
const auth = getAuth();
const challenge = message.challenge;
if (!auth || !challenge || getChallengeId(challenge) === null) {
return null;
}
const ownName = normalizeUsername(auth.username);
const challenger = normalizeUsername(getChallengeChallenger(challenge, message.sender));
const opponent = normalizeUsername(getChallengeOpponent(challenge, message.recipient));
if (ownName === opponent) {
return "opponent";
}
if (ownName === challenger) {
return "challenger";
}
return null;
}
function canOpponentAcceptChallenge(message) {
if (message.type !== MESSAGE_TYPE_CHALLENGE || !message.challenge) {
return false;
}
const role = getChallengeRole(message);
const challenge = message.challenge;
const opponentScore = getOpponentScore(challenge);
return role === "opponent" && !hasScore(opponentScore) && !hasScore(challenge.challengerScore);
}
function canChallengerPlayChallenge(message) {
const role = getChallengeRole(message);
const challenge = message.challenge;
return role === "challenger"
&& hasScore(getOpponentScore(challenge))
&& !hasScore(challenge.challengerScore);
}
function startChallenge(message, role) {
const challenge = message.challenge;
const challenger = getChallengeChallenger(challenge, message.sender);
const opponent = getChallengeOpponent(challenge, message.recipient);
const auth = getAuth();
const otherUser = role === "opponent" ? challenger : opponent;
sessionStorage.setItem(
ACTIVE_CHALLENGE_STORAGE_KEY,
JSON.stringify({
id: getChallengeId(challenge),
role: role,
challenger: challenger,
opponent: otherUser,
opponentScore: role === "challenger" ? getOpponentScore(challenge) : null,
ownUsername: auth?.username ?? "",
}),
);
if (typeof window.loadPage === "function") {
window.loadPage("play", "nav-play");
}
}
function normalizeUser(user) {
if (typeof user === "string") {
return user;
@ -218,6 +360,24 @@
item.appendChild(createChallengeResultGraphic(message.challenge));
}
if (canOpponentAcceptChallenge(message)) {
const acceptButton = document.createElement("button");
acceptButton.type = "button";
acceptButton.className = "btn btn-sm mt-3";
acceptButton.textContent = "Challenge annehmen";
acceptButton.addEventListener("click", () => startChallenge(message, "opponent"));
item.appendChild(acceptButton);
}
if (canChallengerPlayChallenge(message)) {
const playButton = document.createElement("button");
playButton.type = "button";
playButton.className = "btn btn-sm mt-3";
playButton.textContent = "Challenge spielen";
playButton.addEventListener("click", () => startChallenge(message, "challenger"));
item.appendChild(playButton);
}
messageList.appendChild(item);
});
}
@ -343,26 +503,11 @@
return;
}
const challenge = result.body || {};
sessionStorage.setItem(
ACTIVE_CHALLENGE_STORAGE_KEY,
JSON.stringify({
id: challenge.id,
challenger: challenge.challenger || auth.username,
opponent: challenge.opponent || recipient,
opponentScore: challenge.opponentScore ?? null,
}),
sessionStorage.removeItem(ACTIVE_CHALLENGE_STORAGE_KEY);
setFeedback(
"Challenge an " + recipient + " wurde gesendet. Der Gegner spielt zuerst; danach bekommst du sein Resultat.",
"success",
);
const scoreText = challenge.opponentScore !== undefined && challenge.opponentScore !== null
? " Gegner-Score: " + challenge.opponentScore + "."
: "";
setFeedback("Challenge an " + recipient + " wurde erstellt." + scoreText + " Du wirst zum Spiel weitergeleitet.", "success");
window.setTimeout(() => {
if (typeof window.loadPage === "function") {
window.loadPage("play", "nav-play");
}
}, 900);
await loadMessages({ showFeedback: false });
}

View File

@ -10,7 +10,7 @@ document.addEventListener("DOMContentLoaded", () => {
if (activeLink) activeLink.classList.add("active");
}
window.loadPage = function loadPage(page, menuId) {
fetch("pages/" + page + ".html")
fetch("pages/" + page + ".html", { cache: "no-store" })
.then(response => {
if (!response.ok) {
throw new Error("HTTP " + response.status);

View File

@ -2,6 +2,7 @@
// --- Konfiguration ---
const MEMORIZE_TIME_SECONDS = 15;
const ACTIVE_CHALLENGE_STORAGE_KEY = "loremIpsumActiveChallenge";
const CHALLENGE_DATA_PREFIX = "[[loremIpsumChallenge:";
// Bausteine fuer den zufaelligen Rundentext. Alles bleibt lokal, damit das Spiel ohne Backend starten kann.
const TEXT_PARTS = {
@ -261,6 +262,14 @@
return new window.ChallengeService(window.config);
}
function getMessageService() {
if (!window.config || !window.MessageService) {
return null;
}
return new window.MessageService(window.config);
}
function readActiveChallenge() {
const raw = sessionStorage.getItem(ACTIVE_CHALLENGE_STORAGE_KEY);
if (!raw) {
@ -269,7 +278,7 @@
try {
const challenge = JSON.parse(raw);
if (!challenge || !challenge.id || !challenge.opponent) {
if (!challenge || challenge.id === undefined || challenge.id === null || !challenge.opponent) {
return null;
}
@ -279,6 +288,21 @@
}
}
function hasActiveChallenge() {
return Boolean(activeChallenge && activeChallenge.id !== undefined && activeChallenge.id !== null);
}
function isChallengeSecondRound() {
return hasActiveChallenge()
&& activeChallenge.role === "challenger"
&& activeChallenge.opponentScore !== null
&& activeChallenge.opponentScore !== undefined;
}
function isChallengeFirstRound() {
return hasActiveChallenge() && activeChallenge.role === "opponent";
}
function showScoreSaveFeedback(message, type) {
if (!scoreSaveFeedback) return;
@ -345,7 +369,7 @@
}
function renderChallengeResult(score) {
if (!scoreSaveFeedback || !activeChallenge) {
if (!scoreSaveFeedback || !isChallengeSecondRound()) {
return;
}
@ -398,7 +422,7 @@
async function completeChallenge(scoreData) {
const auth = getAuth();
if (!auth || !activeChallenge) {
if (!auth || !hasActiveChallenge()) {
return;
}
@ -423,18 +447,77 @@
if (!result.ok) {
showScoreSaveFeedback(
"Score gespeichert, aber die Challenge konnte nicht abgeschlossen werden (Status " + result.status + ").",
"warning",
"Challenge konnte nicht abgeschlossen werden (Status " + result.status + ").",
"danger",
);
return;
}
sessionStorage.removeItem(ACTIVE_CHALLENGE_STORAGE_KEY);
activeChallenge = null;
showScoreSaveFeedback(
"Challenge abgeschlossen. Beide User erhalten eine Ergebnisnachricht.",
"success",
);
if (typeof window.updateMessagesNavState === "function") {
window.updateMessagesNavState();
}
}
async function finishFirstChallengeRound(scoreData) {
await saveScore(scoreData);
await notifyChallenger(scoreData);
sessionStorage.removeItem(ACTIVE_CHALLENGE_STORAGE_KEY);
activeChallenge = null;
showScoreSaveFeedback("Resultat wurde an den Herausforderer gesendet.", "success");
if (typeof window.updateMessagesNavState === "function") {
window.updateMessagesNavState();
}
}
async function notifyChallenger(scoreData) {
const auth = getAuth();
const messageService = getMessageService();
if (!auth || !messageService || !activeChallenge.challenger) {
return;
}
const challengeData = {
id: activeChallenge.id,
challenger: activeChallenge.challenger,
opponent: auth.username,
opponentScore: scoreData.score,
};
const messageText =
CHALLENGE_DATA_PREFIX +
JSON.stringify(challengeData) +
"]]" +
"\n" +
auth.username +
" hat gespielt und " +
scoreData.score +
" Punkte erreicht. Jetzt bist du dran.";
const result = await messageService.postMessage(
auth.username,
auth.password,
activeChallenge.challenger,
"challenge",
messageText,
);
if (!result.ok) {
showScoreSaveFeedback(
"Challenge wurde gespeichert, aber die Nachricht an den Herausforderer konnte nicht gesendet werden.",
"warning",
);
}
}
async function submitScore() {
if (!userTextInput) return;
@ -477,8 +560,15 @@
console.log("Score bereit zum Senden:", scoreData);
try {
await saveScore(scoreData);
await completeChallenge(scoreData);
if (isChallengeFirstRound()) {
showScoreSaveFeedback("Resultat wird an den Herausforderer gesendet...", "info");
await finishFirstChallengeRound(scoreData);
} else if (hasActiveChallenge()) {
showScoreSaveFeedback("Challenge-Resultat wird gesendet...", "info");
await completeChallenge(scoreData);
} else {
await saveScore(scoreData);
}
} catch (error) {
console.error("Fehler beim Speichern des Scores:", error);
showScoreSaveFeedback(
@ -522,7 +612,10 @@
const opponentScore = activeChallenge.opponentScore !== null && activeChallenge.opponentScore !== undefined
? " Score: " + activeChallenge.opponentScore + "."
: "";
challengeHint.textContent = "Challenge gegen " + activeChallenge.opponent + "." + opponentScore;
const hintPrefix = activeChallenge.role === "opponent"
? "Du spielst zuerst gegen "
: "Finale Runde gegen ";
challengeHint.textContent = hintPrefix + activeChallenge.opponent + "." + opponentScore;
challengeHint.classList.remove("d-none");
}

View File

@ -25,7 +25,7 @@
<select id="challenge-recipient" class="form-select mb-3"></select>
<label for="challenge-text" class="form-label">Nachricht</label>
<textarea id="challenge-text" class="form-control mb-3" rows="3">Ich fordere dich zu einer Lorem-Ipsum-Challenge heraus!</textarea>
<textarea id="challenge-text" class="form-control mb-3" rows="3">Kannst du mich schlagen? Spiel eine Runde, dann bin ich dran.</textarea>
<button class="btn" type="submit">Challenge senden</button>
</form>