@@ -104,7 +104,7 @@ function rpcn_build_svg(array $data, string $gradId): string
104104 return $ out ;
105105}
106106
107- // Pre-compute all chart ranges
107+ // Pre-compute all chart ranges
108108$ chartPeriodsMeta = ['48h ' => '48H ' , '1w ' => '1W ' , '1m ' => '1M ' , '3m ' => '3M ' , '6m ' => '6M ' , '1y ' => '1Y ' ];
109109$ chartSvgs = [];
110110foreach ($ chartPeriodsMeta as $ key => $ label ) {
@@ -114,19 +114,10 @@ function rpcn_build_svg(array $data, string $gradId): string
114114 $ chartSvgs [$ key ] = rpcn_build_svg ($ filtered , 'rpcn-grad- ' . $ key );
115115}
116116
117- // Pre-render leaderboard boards server-side
118- $ boardContents = [];
119- $ autoShowBoard = null ;
120- if ($ hasLeaderboard && !empty ($ boards )) {
121- foreach ($ boards as $ boardId => $ boardName ) {
122- ob_start ();
123- $ rpcn_game ->handle_ajax ($ commId , (string )$ boardId );
124- $ boardContents [(int )$ boardId ] = ob_get_clean ();
125- }
126- if (count ($ boards ) === 1 ) {
127- $ autoShowBoard = (int )array_key_first ($ boards );
128- }
129- }
117+ // Leaderboard boards
118+ $ autoShowBoard = ($ hasLeaderboard && !empty ($ boards ) && count ($ boards ) === 1 )
119+ ? (int )array_key_first ($ boards )
120+ : null ;
130121?>
131122<!DOCTYPE html>
132123<html lang="en">
@@ -468,13 +459,8 @@ function rpcn_build_svg(array $data, string $gradId): string
468459 transform: translateY(-1px);
469460 }
470461
471- /* Board selection & views */
472- #rpcn-board-selection { display: none; }
473- .rpcn-board-view { display: none; }
474- #board-list:checked ~ #rpcn-board-selection { display: block; }
475- <?php foreach (array_keys ($ boardContents ) as $ bid ): ?>
476- #board-<?= (int )$ bid ?> :checked ~ #board-view-<?= (int )$ bid ?> { display: block; }
477- <?php endforeach ; ?>
462+ /* Board selection & view panes */
463+ #rpcn-board-view { display: none; }
478464
479465 .rpcn-btn-back-lb {
480466 display: inline-flex;
@@ -540,6 +526,28 @@ function rpcn_build_svg(array $data, string $gradId): string
540526 }
541527 .rpcn-no-scores { color: var(--rpcn-subtle); padding: 20px 0; text-align: center; font-style: italic; }
542528
529+ /* Loading spinner */
530+ .rpcn-lb-loading {
531+ display: flex;
532+ align-items: center;
533+ justify-content: center;
534+ gap: 10px;
535+ padding: 50px 0;
536+ color: var(--rpcn-muted);
537+ font-size: .88rem;
538+ }
539+ .rpcn-lb-loading::before {
540+ content: '';
541+ width: 18px;
542+ height: 18px;
543+ border: 2px solid var(--rpcn-border);
544+ border-top: 2px solid var(--rpcn-accent);
545+ border-radius: 50%;
546+ animation: rpcn-spin .7s linear infinite;
547+ flex-shrink: 0;
548+ }
549+ @keyframes rpcn-spin { to { transform: rotate(360deg); } }
550+
543551 @media (max-width: 640px) {
544552 .rpcn-game { flex-direction: column; }
545553 .rpcn-game-icon,
@@ -559,8 +567,8 @@ function rpcn_build_svg(array $data, string $gradId): string
559567 <!-- Game header -->
560568 <div class="rpcn-game">
561569 <div class="rpcn-game-icon-wrap">
562- <img src="<?= htmlspecialchars ($ gameIcon ) ?> "
563- alt="Game Icon"
570+ <img src="<?= htmlspecialchars ($ gameIcon ) ?> "
571+ alt="Game Icon"
564572 class="rpcn-game-icon"
565573 onerror="this.src='/cdn/rpcn/icon0/default.png'">
566574 </div>
@@ -637,43 +645,98 @@ class="rpcn-game-icon"
637645
638646 </div><!-- /#panel-charts -->
639647
640- <!-- Leaderboards tab -->
641- <?php if ($ hasLeaderboard && !empty ($ boardContents )): ?>
648+ <!-- Leaderboards tab -->
649+ <?php if ($ hasLeaderboard && !empty ($ boards )): ?>
642650 <div id="panel-lb" class="rpcn-tab-panel">
643651
644- <input type="radio" name="rpcn-board" id="board-list" class="rpcn-sr"
645- <?= $ autoShowBoard === null ? 'checked ' : '' ?> >
646- <?php foreach (array_keys ($ boardContents ) as $ bid ): ?>
647- <input type="radio" name="rpcn-board" id="board-<?= (int )$ bid ?> " class="rpcn-sr"
648- <?= $ autoShowBoard === (int )$ bid ? 'checked ' : '' ?> >
649- <?php endforeach ; ?>
650-
651652 <!-- Board selection grid -->
652653 <div id="rpcn-board-selection">
654+ <?php if (count ($ boards ) > 1 ): ?>
653655 <p class="rpcn-lb-section-title">Choose Scoreboard</p>
656+ <?php endif ; ?>
654657 <div class="rpcn-board-grid">
655658 <?php foreach ($ boards as $ boardId => $ boardName ): ?>
656- <label for="board-<?= (int )$ boardId ?> " class="rpcn-board-btn">
659+ <button class="rpcn-board-btn"
660+ data-board-id="<?= (int )$ boardId ?> "
661+ data-board-name="<?= htmlspecialchars ($ boardName ) ?> ">
657662 <?= htmlspecialchars ($ boardName ) ?>
658- </label >
663+ </button >
659664 <?php endforeach ; ?>
660665 </div>
661666 </div>
662667
663- <?php foreach ( $ boardContents as $ bid => $ content ): ? >
664- <div id="board-view- <?= ( int ) $ bid ?> " class=" rpcn-board-view">
665- <?php if ($ autoShowBoard === null ): ?>
666- <label for="board-list" class ="rpcn-btn- back-lb">Back to boards</label >
668+ <!-- Board content pane (shown after a board is selected) -- >
669+ <div id="rpcn-board-view">
670+ <?php if (count ( $ boards ) > 1 ): ?>
671+ <button class="rpcn-btn-back-lb" id ="rpcn-back-lb">← Back to boards</button >
667672 <?php endif ; ?>
668- <?= $ content ? >
673+ <div id="rpcn-board- content"></div >
669674 </div>
670- <?php endforeach ; ?>
671675
672676 </div><!-- /#panel-lb -->
673677 <?php endif ; ?>
674678
675679 </div><!-- /.rpcn-tab-panels -->
676680
677681</div><!-- /.rpcn-page-wrap -->
682+ <script>
683+ (function () {
684+ const commId = <?= json_encode ($ commId ) ?> ;
685+ const autoBoard = <?= json_encode ($ autoShowBoard ) ?> ;
686+ const multiBoard = <?= json_encode (count ($ boards ) > 1 ) ?> ;
687+
688+ const selection = document.getElementById('rpcn-board-selection');
689+ const view = document.getElementById('rpcn-board-view');
690+ const content = document.getElementById('rpcn-board-content');
691+ const backBtn = document.getElementById('rpcn-back-lb');
692+
693+ if (!selection || !view || !content) return;
694+
695+ function loadBoard(boardId) {
696+ selection.style.display = 'none';
697+ view.style.display = 'block';
698+ content.innerHTML = '<div class="rpcn-lb-loading">Loading scores</div>';
699+
700+ fetch('rpcn-game.php?comm_id=' + encodeURIComponent(commId) + '&ajax=1&board_id=' + encodeURIComponent(boardId))
701+ .then(function (r) {
702+ if (!r.ok) throw new Error('HTTP ' + r.status);
703+ return r.text();
704+ })
705+ .then(function (html) {
706+ content.innerHTML = html;
707+ })
708+ .catch(function () {
709+ content.innerHTML = "<p class='rpcn-error'>Failed to load scores. Please try again.</p>";
710+ });
711+ }
712+
713+ document.querySelectorAll('.rpcn-board-btn').forEach(function (btn) {
714+ btn.addEventListener('click', function () {
715+ loadBoard(btn.dataset.boardId);
716+ });
717+ });
718+
719+ // Back button
720+ if (backBtn) {
721+ backBtn.addEventListener('click', function () {
722+ view.style.display = 'none';
723+ content.innerHTML = '';
724+ selection.style.display = 'block';
725+ });
726+ }
727+
728+ // Autoload when there is only one board
729+ if (autoBoard !== null) {
730+ // Trigger when user switches to the Leaderboards tab
731+ var tabLb = document.getElementById('tab-lb');
732+ if (tabLb) {
733+ var loaded = false;
734+ tabLb.addEventListener('change', function () {
735+ if (!loaded) { loaded = true; loadBoard(autoBoard); }
736+ });
737+ }
738+ }
739+ })();
740+ </script>
678741</body>
679742</html>
0 commit comments