diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index 78cb015..3d81acc 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -178,8 +178,67 @@ class Scoresheet { } this.game.appendChild(this.tetsuoHana) } + + // Add leaderboard submit button if user is logged in + if (account.loggedIn && !this.multiplayer) { + this.leaderboardBtn = document.createElement("div") + this.leaderboardBtn.id = "leaderboard-submit-btn" + this.leaderboardBtn.innerHTML = "🏆 提交排行榜
Submit to Leaderboard" + this.leaderboardBtn.style.cssText = ` + position: fixed; + bottom: 20px; + right: 20px; + padding: 15px 25px; + background: linear-gradient(135deg, #ff6b9d, #c44db3); + color: white; + border-radius: 15px; + font-size: 18px; + font-family: ${strings.font || "sans-serif"}; + text-align: center; + cursor: pointer; + z-index: 1000; + box-shadow: 0 4px 15px rgba(0,0,0,0.3); + border: 3px solid #fff; + transition: all 0.2s ease; + user-select: none; + ` + this.leaderboardBtn.onmouseover = () => { + if (!this.leaderboardSubmitted) { + this.leaderboardBtn.style.transform = "scale(1.05)" + this.leaderboardBtn.style.boxShadow = "0 6px 20px rgba(0,0,0,0.4)" + } + } + this.leaderboardBtn.onmouseout = () => { + this.leaderboardBtn.style.transform = "scale(1)" + this.leaderboardBtn.style.boxShadow = "0 4px 15px rgba(0,0,0,0.3)" + } + this.leaderboardBtn.onclick = () => this.onLeaderboardBtnClick() + this.game.appendChild(this.leaderboardBtn) + } } + onLeaderboardBtnClick() { + if (this.leaderboardSubmitted) { + return + } + if (!this.leaderboardData || !this.leaderboardData.songId) { + this.showLeaderboardNotification("no_song_id") + return + } + + // Disable button and show submitting state + this.leaderboardBtn.innerHTML = "⏳ 提交中..." + this.leaderboardBtn.style.background = "linear-gradient(135deg, #888, #666)" + this.leaderboardBtn.style.cursor = "default" + + this.submitToLeaderboard( + this.leaderboardData.songId, + this.leaderboardData.difficulty, + this.leaderboardData.scoreObj + ) + } + + redraw() { if (!this.redrawRunning) { return @@ -933,6 +992,15 @@ class Scoresheet { if (clearReached) { crown = this.resultsObj.bad === 0 ? "gold" : "silver" } + + // Store data for manual leaderboard submission + this.leaderboardData = { + songId: songId, + difficulty: difficulty, + scoreObj: Object.assign({}, this.resultsObj) + } + this.leaderboardSubmitted = false + if (!oldScore || oldScore.points <= this.resultsObj.points) { if (oldScore && (oldScore.crown === "gold" || oldScore.crown === "silver" && !crown)) { crown = oldScore.crown @@ -941,10 +1009,7 @@ class Scoresheet { delete this.resultsObj.title delete this.resultsObj.difficulty delete this.resultsObj.gauge - scoreStorage.add(hash, difficulty, this.resultsObj, true, title).then(() => { - // Auto-submit to leaderboard if logged in and has song ID - this.submitToLeaderboard(songId, difficulty, this.resultsObj) - }).catch(() => { + scoreStorage.add(hash, difficulty, this.resultsObj, true, title).catch(() => { this.showWarning = { name: "scoreSaveFailed" } }) } else if (oldScore && (crown === "gold" && oldScore.crown !== "gold" || crown && !oldScore.crown)) { @@ -957,13 +1022,13 @@ class Scoresheet { this.scoreSaved = true } + submitToLeaderboard(songId, difficulty, scoreObj) { // Only submit if user is logged in and song has an ID if (!account.loggedIn || !songId) { return } - var self = this loader.getCsrfToken().then(token => { var request = new XMLHttpRequest() @@ -976,11 +1041,39 @@ class Scoresheet { try { var response = JSON.parse(request.responseText) if (response.status === "ok") { + self.leaderboardSubmitted = true self.showLeaderboardNotification(response.message) + // Update button to show success + if (self.leaderboardBtn) { + self.leaderboardBtn.innerHTML = "✅ 已提交
Submitted!" + self.leaderboardBtn.style.background = "linear-gradient(135deg, #4CAF50, #45a049)" + } + } else { + // Show error + self.showLeaderboardNotification("error") + if (self.leaderboardBtn) { + self.leaderboardBtn.innerHTML = "❌ 失败
Failed" + self.leaderboardBtn.style.background = "linear-gradient(135deg, #f44336, #d32f2f)" + } } } catch (e) { console.error("Failed to parse leaderboard response:", e) + self.showLeaderboardNotification("error") } + } else { + self.showLeaderboardNotification("error") + if (self.leaderboardBtn) { + self.leaderboardBtn.innerHTML = "❌ 失败
Failed" + self.leaderboardBtn.style.background = "linear-gradient(135deg, #f44336, #d32f2f)" + } + } + } + + request.onerror = function () { + self.showLeaderboardNotification("error") + if (self.leaderboardBtn) { + self.leaderboardBtn.innerHTML = "❌ 失败
Failed" + self.leaderboardBtn.style.background = "linear-gradient(135deg, #f44336, #d32f2f)" } } @@ -991,9 +1084,15 @@ class Scoresheet { })) }).catch(() => { console.log("Leaderboard submission failed") + this.showLeaderboardNotification("error") + if (this.leaderboardBtn) { + this.leaderboardBtn.innerHTML = "❌ 失败
Failed" + this.leaderboardBtn.style.background = "linear-gradient(135deg, #f44336, #d32f2f)" + } }) } + showLeaderboardNotification(message) { var notification = document.createElement("div") notification.className = "leaderboard-notification" @@ -1020,6 +1119,8 @@ class Scoresheet { case "score_updated": text = "🎉 排行榜成绩已更新!"; break case "score_not_higher": text = "📊 已有更高成绩"; break case "score_too_low": text = "未进入排行榜前50"; break + case "error": text = "❌ 提交失败,请重试"; break + case "no_song_id": text = "❌ 无法提交:歌曲ID缺失"; break default: text = "排行榜已更新" } notification.innerText = text @@ -1063,10 +1164,17 @@ class Scoresheet { if (!this.multiplayer) { delete this.tetsuoHana } + // Clean up leaderboard button + if (this.leaderboardBtn && this.leaderboardBtn.parentNode) { + this.leaderboardBtn.parentNode.removeChild(this.leaderboardBtn) + } + delete this.leaderboardBtn + delete this.leaderboardData delete this.ctx delete this.canvas delete this.fadeScreen delete this.results delete this.rules } + }