Commit a032b00e authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: wire emoji() theming across entire player app — all registered emojis now replaceable

Every emoji that has a slot in admin branding now goes through the emoji()
function from core/theme.js. Uploading a PNG/SVG replacement in admin will
now show up everywhere that emoji appears in the player app.

Files updated: hud, multiplayer, player-panel, chess (game/analysis/review/
result/history), ludo (game/result), domino (result), play table, daily
rewards, challenges, ranks, shop, leaderboard, tournaments, friends,
activity, org (home/browser), profile (view/settings), puzzles.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 1733e6bd
...@@ -2,6 +2,7 @@ import * as store from './store.js'; ...@@ -2,6 +2,7 @@ import * as store from './store.js';
import * as bus from './bus.js'; import * as bus from './bus.js';
import * as scene from './scene.js'; import * as scene from './scene.js';
import { t } from './i18n.js'; import { t } from './i18n.js';
import { emoji } from './theme.js';
let hudEl, tabBar; let hudEl, tabBar;
...@@ -31,15 +32,15 @@ function renderHud() { ...@@ -31,15 +32,15 @@ function renderHud() {
<div class="hud-brand">EL3AB</div> <div class="hud-brand">EL3AB</div>
<div class="hud-stats"> <div class="hud-stats">
<div class="hud-stat hud-level"> <div class="hud-stat hud-level">
<span class="icon"></span> <span class="icon">${emoji('hud_level', '⬡', 16)}</span>
<span id="hud-level">${player?.level || 1}</span> <span id="hud-level">${player?.level || 1}</span>
</div> </div>
<div class="hud-stat hud-coins"> <div class="hud-stat hud-coins">
<span class="icon"></span> <span class="icon">${emoji('hud_coin', '●', 16)}</span>
<span id="hud-coins">${formatNum(player?.coins || 0)}</span> <span id="hud-coins">${formatNum(player?.coins || 0)}</span>
</div> </div>
<div class="hud-stat hud-gems"> <div class="hud-stat hud-gems">
<span class="icon"></span> <span class="icon">${emoji('hud_gem', '◆', 16)}</span>
<span id="hud-gems">${formatNum(player?.gems || 0)}</span> <span id="hud-gems">${formatNum(player?.gems || 0)}</span>
</div> </div>
</div> </div>
......
...@@ -5,6 +5,7 @@ import * as net from './net.js'; ...@@ -5,6 +5,7 @@ import * as net from './net.js';
import * as store from './store.js'; import * as store from './store.js';
import * as audio from './audio.js'; import * as audio from './audio.js';
import * as juice from './juice.js'; import * as juice from './juice.js';
import { emoji } from './theme.js';
let currentMatchId = null; let currentMatchId = null;
let currentMatchType = null; // 'chess' | 'ludo' | 'domino' let currentMatchType = null; // 'chess' | 'ludo' | 'domino'
...@@ -24,13 +25,13 @@ export function renderOpponentBar(container, opponent, options = {}) { ...@@ -24,13 +25,13 @@ export function renderOpponentBar(container, opponent, options = {}) {
bar.innerHTML = ` bar.innerHTML = `
<div style="position:relative;"> <div style="position:relative;">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden;"> <div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden;">
${opponent.avatar_url ? `<img src="${opponent.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : '👤'} ${opponent.avatar_url ? `<img src="${opponent.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 14)}
</div> </div>
<div id="mp-conn-dot" style="position:absolute;bottom:-1px;right:-1px;width:10px;height:10px;border-radius:50%;background:#34D399;border:2px solid #0f0f1e;"></div> <div id="mp-conn-dot" style="position:absolute;bottom:-1px;right:-1px;width:10px;height:10px;border-radius:50%;background:#34D399;border:2px solid #0f0f1e;"></div>
</div> </div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:600;color:#f8fafc;">${opponent.display_name || opponent.username || 'خصم'}</div> <div style="font-size:13px;font-weight:600;color:#f8fafc;">${opponent.display_name || opponent.username || 'خصم'}</div>
${showRating ? `<div style="font-size:11px;color:#64748b;"> ${opponent.rating || opponent.elo_rapid || '1200'}</div>` : ''} ${showRating ? `<div style="font-size:11px;color:#64748b;">${emoji('star', '⭐', 11)} ${opponent.rating || opponent.elo_rapid || '1200'}</div>` : ''}
</div> </div>
<div id="mp-opponent-status" style="font-size:10px;color:#64748b;"></div> <div id="mp-opponent-status" style="font-size:10px;color:#64748b;"></div>
`; `;
...@@ -55,7 +56,7 @@ function showOpponentActions(container, opponent) { ...@@ -55,7 +56,7 @@ function showOpponentActions(container, opponent) {
menu.id = 'mp-opponent-menu'; menu.id = 'mp-opponent-menu';
menu.style.cssText = 'position:absolute;top:50px;right:12px;background:#1a1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:8px;z-index:100;box-shadow:0 8px 24px rgba(0,0,0,0.5);display:flex;flex-direction:column;gap:4px;min-width:140px;'; menu.style.cssText = 'position:absolute;top:50px;right:12px;background:#1a1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:8px;z-index:100;box-shadow:0 8px 24px rgba(0,0,0,0.5);display:flex;flex-direction:column;gap:4px;min-width:140px;';
menu.innerHTML = ` menu.innerHTML = `
<button class="mp-action" data-action="profile">👤 الملف الشخصي</button> <button class="mp-action" data-action="profile">${emoji('person', '👤', 12)} الملف الشخصي</button>
<button class="mp-action" data-action="friend">➕ إضافة صديق</button> <button class="mp-action" data-action="friend">➕ إضافة صديق</button>
<button class="mp-action" data-action="report" style="color:#EF4444;">⚠️ إبلاغ</button> <button class="mp-action" data-action="report" style="color:#EF4444;">⚠️ إبلاغ</button>
`; `;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import * as audio from './audio.js'; import * as audio from './audio.js';
import * as mp from './multiplayer.js'; import * as mp from './multiplayer.js';
import { getTier } from '../modules/rewards/scenes/ranks.js'; import { getTier } from '../modules/rewards/scenes/ranks.js';
import { emoji } from './theme.js';
export function render(container, player, options = {}) { export function render(container, player, options = {}) {
const { position = 'top', isActive = false, showRating = true, showLevel = true, isSelf = false } = options; const { position = 'top', isActive = false, showRating = true, showLevel = true, isSelf = false } = options;
...@@ -17,7 +18,7 @@ export function render(container, player, options = {}) { ...@@ -17,7 +18,7 @@ export function render(container, player, options = {}) {
<div class="pp-avatar"> <div class="pp-avatar">
${player.avatar_url ${player.avatar_url
? `<img src="${player.avatar_url}" alt="">` ? `<img src="${player.avatar_url}" alt="">`
: `<span class="pp-avatar-fallback">${isSelf ? '👤' : '🎮'}</span>` : `<span class="pp-avatar-fallback">${isSelf ? emoji('person', '👤', 16) : emoji('gamepad', '🎮', 16)}</span>`
} }
<div class="pp-status-dot ${player.isOnline !== false ? 'online' : ''}"></div> <div class="pp-status-dot ${player.isOnline !== false ? 'online' : ''}"></div>
</div> </div>
...@@ -78,7 +79,7 @@ function showPlayerPopup(container, profile) { ...@@ -78,7 +79,7 @@ function showPlayerPopup(container, profile) {
popup.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#1a1a2e;border:1px solid rgba(255,255,255,0.1);border-radius:16px;padding:20px;z-index:200;box-shadow:0 12px 40px rgba(0,0,0,0.7);text-align:center;min-width:220px;'; popup.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#1a1a2e;border:1px solid rgba(255,255,255,0.1);border-radius:16px;padding:20px;z-index:200;box-shadow:0 12px 40px rgba(0,0,0,0.7);text-align:center;min-width:220px;';
popup.innerHTML = ` popup.innerHTML = `
<div style="width:56px;height:56px;border-radius:50%;background:#2a2a4a;margin:0 auto 10px;display:flex;align-items:center;justify-content:center;font-size:26px;overflow:hidden;"> <div style="width:56px;height:56px;border-radius:50%;background:#2a2a4a;margin:0 auto 10px;display:flex;align-items:center;justify-content:center;font-size:26px;overflow:hidden;">
${profile.avatar_url ? `<img src="${profile.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : '👤'} ${profile.avatar_url ? `<img src="${profile.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 26)}
</div> </div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;">${profile.display_name || profile.username}</div> <div style="font-size:16px;font-weight:700;color:#f8fafc;">${profile.display_name || profile.username}</div>
<div style="font-size:12px;color:#64748b;margin:4px 0 16px;">Level ${profile.level || 1} · ${profile.elo_rapid || 1200} ELO</div> <div style="font-size:12px;color:#64748b;margin:4px 0 16px;">Level ${profile.level || 1} · ${profile.elo_rapid || 1200} ELO</div>
......
...@@ -8,6 +8,7 @@ import * as engine from '../logic/engine.js'; ...@@ -8,6 +8,7 @@ import * as engine from '../logic/engine.js';
import { lookup, renderExplorerBar } from '../logic/explorer.js'; import { lookup, renderExplorerBar } from '../logic/explorer.js';
import { classifyMove } from '../logic/classifier.js'; import { classifyMove } from '../logic/classifier.js';
import { getOpeningName } from '../logic/openings.js'; import { getOpeningName } from '../logic/openings.js';
import { emoji } from '../../../core/theme.js';
let board, analysisData, currentMoveIdx; let board, analysisData, currentMoveIdx;
...@@ -22,7 +23,7 @@ export function mountAnalysis(el, params) { ...@@ -22,7 +23,7 @@ export function mountAnalysis(el, params) {
<div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;"> <div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">← ${t('game.back')}</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">← ${t('game.back')}</button>
<span style="font-size:14px;font-weight:700;color:#f8fafc;">📊 تحليل المباراة</span> <span style="font-size:14px;font-weight:700;color:#f8fafc;">${emoji('chart', '📊', 14)} تحليل المباراة</span>
<div style="width:60px;"></div> <div style="width:60px;"></div>
</div> </div>
......
...@@ -12,6 +12,7 @@ import { getOpeningName } from '../logic/openings.js'; ...@@ -12,6 +12,7 @@ import { getOpeningName } from '../logic/openings.js';
import { getMaterialAdvantage, formatAdvantage } from '../logic/material.js'; import { getMaterialAdvantage, formatAdvantage } from '../logic/material.js';
import * as emoteSystem from '../components/emotes.js'; import * as emoteSystem from '../components/emotes.js';
import * as mp from '../../../core/multiplayer.js'; import * as mp from '../../../core/multiplayer.js';
import { emoji } from '../../../core/theme.js';
let board, clock, gameState; let board, clock, gameState;
...@@ -39,7 +40,7 @@ export function mountGame(el, params) { ...@@ -39,7 +40,7 @@ export function mountGame(el, params) {
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:#0f0f1e;"> <div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div style="display:flex;align-items:center;gap:8px;"> <div style="display:flex;align-items:center;gap:8px;">
<div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;"> <div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;">
<img src="https://stockfishapi.caprover.al-arcade.com/portraits/${botId || 'amina'}.png" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';this.parentNode.textContent='🤖'"> <img src="https://stockfishapi.caprover.al-arcade.com/portraits/${botId || 'amina'}.png" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';this.parentNode.innerHTML='${emoji('robot', '🤖', 14).replace(/'/g, "\\'")}'">
</div> </div>
<div> <div>
<div style="font-size:13px;font-weight:600;color:#f8fafc;" id="opponent-name">${mode === 'bot' ? (botId || 'Bot') : 'Opponent'}</div> <div style="font-size:13px;font-weight:600;color:#f8fafc;" id="opponent-name">${mode === 'bot' ? (botId || 'Bot') : 'Opponent'}</div>
...@@ -60,7 +61,7 @@ export function mountGame(el, params) { ...@@ -60,7 +61,7 @@ export function mountGame(el, params) {
<!-- Player Bar --> <!-- Player Bar -->
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:#0f0f1e;"> <div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div style="display:flex;align-items:center;gap:8px;"> <div style="display:flex;align-items:center;gap:8px;">
<div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;color:#f8fafc;">👤</div> <div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;color:#f8fafc;">${emoji('person', '👤', 14)}</div>
<div> <div>
<div style="font-size:13px;font-weight:600;color:#f8fafc;">${store.get('player.username') || store.get('player.display_name') || 'You'}</div> <div style="font-size:13px;font-weight:600;color:#f8fafc;">${store.get('player.username') || store.get('player.display_name') || 'You'}</div>
<div id="player-captured" style="font-size:11px;color:#94a3b8;min-height:14px;letter-spacing:1px;"></div> <div id="player-captured" style="font-size:11px;color:#94a3b8;min-height:14px;letter-spacing:1px;"></div>
...@@ -81,7 +82,7 @@ export function mountGame(el, params) { ...@@ -81,7 +82,7 @@ export function mountGame(el, params) {
<!-- Controls --> <!-- Controls -->
<div style="display:flex;gap:6px;padding:8px 12px;background:#0f0f1e;border-top:1px solid rgba(255,255,255,0.05);"> <div style="display:flex;gap:6px;padding:8px 12px;background:#0f0f1e;border-top:1px solid rgba(255,255,255,0.05);">
<button class="ctrl-btn" id="btn-resign"> ${t('game.resign')}</button> <button class="ctrl-btn" id="btn-resign">${emoji('flag', '⚐', 12)} ${t('game.resign')}</button>
<button class="ctrl-btn" id="btn-draw">½ ${t('game.draw')}</button> <button class="ctrl-btn" id="btn-draw">½ ${t('game.draw')}</button>
<button class="ctrl-btn" id="btn-flip">⟲ ${t('game.flip')}</button> <button class="ctrl-btn" id="btn-flip">⟲ ${t('game.flip')}</button>
</div> </div>
...@@ -212,7 +213,7 @@ export function mountGame(el, params) { ...@@ -212,7 +213,7 @@ export function mountGame(el, params) {
mp.onEmoteReceived((emote) => { mp.onEmoteReceived((emote) => {
const boardContainer = el.querySelector('#board-container'); const boardContainer = el.querySelector('#board-container');
const oppBar = el.querySelector('.chess-bar'); // opponent bar is first chess-bar const oppBar = el.querySelector('.chess-bar'); // opponent bar is first chess-bar
emoteSystem.showReceived(boardContainer, emote.key === 'gg' ? '🤝' : emote.key === 'good_move' ? '👏' : '😮', oppBar); emoteSystem.showReceived(boardContainer, emote.key === 'gg' ? emoji('handshake', '🤝', 24) : emote.key === 'good_move' ? '👏' : '😮', oppBar);
audio.play('notification'); audio.play('notification');
}); });
} }
...@@ -523,7 +524,7 @@ function startLivePolling(el) { ...@@ -523,7 +524,7 @@ function startLivePolling(el) {
if (emoteData) { if (emoteData) {
const boardContainer = el.querySelector('#board-container'); const boardContainer = el.querySelector('#board-container');
const oppBar = el.querySelector('.chess-bar'); const oppBar = el.querySelector('.chess-bar');
emoteSystem.showReceived(boardContainer, emoteData.key === 'gg' ? '🤝' : '👏', oppBar); emoteSystem.showReceived(boardContainer, emoteData.key === 'gg' ? emoji('handshake', '🤝', 24) : '👏', oppBar);
audio.play('notification'); audio.play('notification');
} }
......
...@@ -38,7 +38,7 @@ function renderHistory(el, matches) { ...@@ -38,7 +38,7 @@ function renderHistory(el, matches) {
if (matches.length === 0) { if (matches.length === 0) {
list.innerHTML = ` list.innerHTML = `
<div style="text-align:center;padding:40px;"> <div style="text-align:center;padding:40px;">
<div style="font-size:40px;margin-bottom:8px;opacity:0.4;">📋</div> <div style="font-size:40px;margin-bottom:8px;opacity:0.4;">${emoji('clipboard', '📋', 40)}</div>
<div style="font-size:14px;color:#64748b;">لا توجد مباريات بعد — العب لتظهر هنا</div> <div style="font-size:14px;color:#64748b;">لا توجد مباريات بعد — العب لتظهر هنا</div>
</div>`; </div>`;
return; return;
......
...@@ -72,10 +72,10 @@ export function mountResult(el, params) { ...@@ -72,10 +72,10 @@ export function mountResult(el, params) {
<!-- Actions --> <!-- Actions -->
<div style="display:flex;flex-direction:column;gap:8px;width:100%;max-width:280px;margin-top:12px;"> <div style="display:flex;flex-direction:column;gap:8px;width:100%;max-width:280px;margin-top:12px;">
<button class="btn btn-primary w-full" id="btn-rematch" style="font-size:15px;">${t('game.rematch')}</button> <button class="btn btn-primary w-full" id="btn-rematch" style="font-size:15px;">${t('game.rematch')}</button>
<button class="btn btn-secondary w-full" id="btn-analyze" style="font-size:13px;">📊 تحليل المباراة</button> <button class="btn btn-secondary w-full" id="btn-analyze" style="font-size:13px;">${emoji('chart', '📊', 13)} تحليل المباراة</button>
<div style="display:flex;gap:8px;"> <div style="display:flex;gap:8px;">
<button class="btn btn-secondary" id="btn-share" style="flex:1;font-size:12px;">📤 مشاركة PGN</button> <button class="btn btn-secondary" id="btn-share" style="flex:1;font-size:12px;">${emoji('share', '📤', 12)} مشاركة PGN</button>
<button class="btn btn-secondary" id="btn-copy-pgn" style="flex:1;font-size:12px;">📋 نسخ</button> <button class="btn btn-secondary" id="btn-copy-pgn" style="flex:1;font-size:12px;">${emoji('clipboard', '📋', 12)} نسخ</button>
</div> </div>
<button class="btn btn-secondary w-full" id="btn-back" style="font-size:13px;">${t('game.back')}</button> <button class="btn btn-secondary w-full" id="btn-back" style="font-size:13px;">${t('game.back')}</button>
</div> </div>
...@@ -153,7 +153,7 @@ export function mountResult(el, params) { ...@@ -153,7 +153,7 @@ export function mountResult(el, params) {
const btn = el.querySelector('#btn-copy-pgn'); const btn = el.querySelector('#btn-copy-pgn');
btn.textContent = '✓ تم النسخ'; btn.textContent = '✓ تم النسخ';
juice.pulseElement(btn, '#34D399'); juice.pulseElement(btn, '#34D399');
setTimeout(() => { btn.textContent = '📋 نسخ'; }, 2000); setTimeout(() => { btn.innerHTML = `${emoji('clipboard', '📋', 12)} نسخ`; }, 2000);
}); });
}); });
......
...@@ -7,6 +7,7 @@ import { createCanvas, clear } from '../../../core/canvas.js'; ...@@ -7,6 +7,7 @@ import { createCanvas, clear } from '../../../core/canvas.js';
import * as engine from '../logic/engine.js'; import * as engine from '../logic/engine.js';
import { classifyMove, calculateAccuracy, getMoveClassSummary, getAccuracyLabel } from '../logic/classifier.js'; import { classifyMove, calculateAccuracy, getMoveClassSummary, getAccuracyLabel } from '../logic/classifier.js';
import { getOpeningName } from '../logic/openings.js'; import { getOpeningName } from '../logic/openings.js';
import { emoji } from '../../../core/theme.js';
let reviewData = null; let reviewData = null;
...@@ -17,7 +18,7 @@ export async function mountReview(el, params) { ...@@ -17,7 +18,7 @@ export async function mountReview(el, params) {
<div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;overflow-y:auto;"> <div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;overflow-y:auto;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);">
<button class="btn btn-secondary" id="back-btn" style="min-height:30px;padding:4px 12px;font-size:12px;">← ${t('game.back')}</button> <button class="btn btn-secondary" id="back-btn" style="min-height:30px;padding:4px 12px;font-size:12px;">← ${t('game.back')}</button>
<span style="font-size:15px;font-weight:700;color:#f8fafc;"> مراجعة المباراة</span> <span style="font-size:15px;font-weight:700;color:#f8fafc;">${emoji('star', '⭐', 15)} مراجعة المباراة</span>
<div style="width:50px;"></div> <div style="width:50px;"></div>
</div> </div>
...@@ -133,7 +134,7 @@ function renderReview(el, results, moves, playerColor) { ...@@ -133,7 +134,7 @@ function renderReview(el, results, moves, playerColor) {
<!-- Accuracy Comparison --> <!-- Accuracy Comparison -->
<div style="background:#0f0f1e;border-radius:10px;padding:16px;margin-bottom:12px;"> <div style="background:#0f0f1e;border-radius:10px;padding:16px;margin-bottom:12px;">
<div style="text-align:center;font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:12px;"> مراجعة المباراة</div> <div style="text-align:center;font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:12px;">${emoji('star', '⭐', 15)} مراجعة المباراة</div>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;"> <div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;">
<!-- Player accuracy --> <!-- Player accuracy -->
...@@ -186,7 +187,7 @@ function renderReview(el, results, moves, playerColor) { ...@@ -186,7 +187,7 @@ function renderReview(el, results, moves, playerColor) {
</div> </div>
<!-- Back to analysis button --> <!-- Back to analysis button -->
<button class="btn btn-secondary w-full" id="btn-full-analysis" style="font-size:13px;margin-bottom:12px;">📊 التحليل التفصيلي</button> <button class="btn btn-secondary w-full" id="btn-full-analysis" style="font-size:13px;margin-bottom:12px;">${emoji('chart', '📊', 13)} التحليل التفصيلي</button>
`; `;
// Render eval graph // Render eval graph
......
...@@ -9,6 +9,7 @@ import * as juice from '../../../core/juice.js'; ...@@ -9,6 +9,7 @@ import * as juice from '../../../core/juice.js';
import { getPiecePosition, getHomeBasePosition, SAFE_SQUARES, HOME_COLUMNS, SHARED_PATH } from '../logic/board-map.js'; import { getPiecePosition, getHomeBasePosition, SAFE_SQUARES, HOME_COLUMNS, SHARED_PATH } from '../logic/board-map.js';
import * as emoteSystem from '../../chess/components/emotes.js'; import * as emoteSystem from '../../chess/components/emotes.js';
import * as mp from '../../../core/multiplayer.js'; import * as mp from '../../../core/multiplayer.js';
import { emoji } from '../../../core/theme.js';
import * as net from '../../../core/net.js'; import * as net from '../../../core/net.js';
let game, validMoves, ctx, canvas, boardSize, cellSize; let game, validMoves, ctx, canvas, boardSize, cellSize;
...@@ -107,7 +108,7 @@ export function mountGame(el, params) { ...@@ -107,7 +108,7 @@ export function mountGame(el, params) {
// Find which player sent it (not me) // Find which player sent it (not me)
const senderIdx = emote.from === store.get('auth.userId') ? myPlayerIndex : (myPlayerIndex === 0 ? 1 : 0); const senderIdx = emote.from === store.get('auth.userId') ? myPlayerIndex : (myPlayerIndex === 0 ? 1 : 0);
const oppPanel = el.querySelector(`#pp-${senderIdx}`); const oppPanel = el.querySelector(`#pp-${senderIdx}`);
emoteSystem.showReceived(emoteWrap, emote.key === 'gg' ? '🤝' : emote.key === 'good_move' ? '👏' : '😮', oppPanel); emoteSystem.showReceived(emoteWrap, emote.key === 'gg' ? emoji('handshake', '🤝', 24) : emote.key === 'good_move' ? '👏' : '😮', oppPanel);
audio.play('notification'); audio.play('notification');
}); });
...@@ -393,7 +394,7 @@ function showOpponentPopup(el, profile) { ...@@ -393,7 +394,7 @@ function showOpponentPopup(el, profile) {
popup.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#1a1a2e;border:1px solid rgba(255,255,255,0.1);border-radius:16px;padding:20px;z-index:200;box-shadow:0 12px 40px rgba(0,0,0,0.7);text-align:center;min-width:200px;'; popup.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#1a1a2e;border:1px solid rgba(255,255,255,0.1);border-radius:16px;padding:20px;z-index:200;box-shadow:0 12px 40px rgba(0,0,0,0.7);text-align:center;min-width:200px;';
popup.innerHTML = ` popup.innerHTML = `
<div style="width:48px;height:48px;border-radius:50%;background:#2a2a4a;margin:0 auto 8px;display:flex;align-items:center;justify-content:center;font-size:22px;"> <div style="width:48px;height:48px;border-radius:50%;background:#2a2a4a;margin:0 auto 8px;display:flex;align-items:center;justify-content:center;font-size:22px;">
${profile.avatar_url ? `<img src="${profile.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : '👤'} ${profile.avatar_url ? `<img src="${profile.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : emoji('person', '👤', 14)}
</div> </div>
<div style="font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:4px;">${profile.display_name || profile.username}</div> <div style="font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:4px;">${profile.display_name || profile.username}</div>
<div style="font-size:11px;color:#64748b;margin-bottom:14px;">Level ${profile.level || 1}</div> <div style="font-size:11px;color:#64748b;margin-bottom:14px;">Level ${profile.level || 1}</div>
......
...@@ -2,6 +2,7 @@ import * as net from '../../../core/net.js'; ...@@ -2,6 +2,7 @@ import * as net from '../../../core/net.js';
import * as scene from '../../../core/scene.js'; import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js'; import * as audio from '../../../core/audio.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export async function mountBrowser(el) { export async function mountBrowser(el) {
el.innerHTML = ` el.innerHTML = `
...@@ -32,11 +33,11 @@ function renderOrgs(el, orgs) { ...@@ -32,11 +33,11 @@ function renderOrgs(el, orgs) {
list.innerHTML = orgs.map(org => ` list.innerHTML = orgs.map(org => `
<div class="card" data-id="${org.id}" style="display:flex;align-items:center;gap:var(--s-3);padding:var(--s-3);margin-bottom:var(--s-3);cursor:pointer;"> <div class="card" data-id="${org.id}" style="display:flex;align-items:center;gap:var(--s-3);padding:var(--s-3);margin-bottom:var(--s-3);cursor:pointer;">
<div style="width:48px;height:48px;border-radius:var(--r-md);background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;font-size:20px;overflow:hidden;"> <div style="width:48px;height:48px;border-radius:var(--r-md);background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;font-size:20px;overflow:hidden;">
${org.logo_url ? `<img src="${org.logo_url}" style="width:100%;height:100%;object-fit:cover;">` : '🏛️'} ${org.logo_url ? `<img src="${org.logo_url}" style="width:100%;height:100%;object-fit:cover;">` : emoji('building', '🏛️', 20)}
</div> </div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:14px;font-weight:600;">${org.name_ar || org.name}</div> <div style="font-size:14px;font-weight:600;">${org.name_ar || org.name}</div>
<div style="font-size:11px;color:var(--text-secondary);">👥 ${org.member_count || 0} عضو</div> <div style="font-size:11px;color:var(--text-secondary);">${emoji('people', '👥', 11)} ${org.member_count || 0} عضو</div>
</div> </div>
<button class="btn btn-secondary" style="font-size:11px;min-height:32px;padding:var(--s-1) var(--s-3);">انضم</button> <button class="btn btn-secondary" style="font-size:11px;min-height:32px;padding:var(--s-1) var(--s-3);">انضم</button>
</div> </div>
......
...@@ -2,6 +2,7 @@ import * as net from '../../../core/net.js'; ...@@ -2,6 +2,7 @@ import * as net from '../../../core/net.js';
import * as scene from '../../../core/scene.js'; import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js'; import * as audio from '../../../core/audio.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export async function mountHome(el, params) { export async function mountHome(el, params) {
const { orgId } = params; const { orgId } = params;
...@@ -35,7 +36,7 @@ function renderOrg(el, org) { ...@@ -35,7 +36,7 @@ function renderOrg(el, org) {
el.querySelector('#org-content').innerHTML = ` el.querySelector('#org-content').innerHTML = `
<div class="card" style="text-align:center;padding:var(--s-6);"> <div class="card" style="text-align:center;padding:var(--s-6);">
<div style="width:64px;height:64px;border-radius:var(--r-md);background:var(--bg-elevated);margin:0 auto var(--s-3);display:flex;align-items:center;justify-content:center;font-size:28px;"> <div style="width:64px;height:64px;border-radius:var(--r-md);background:var(--bg-elevated);margin:0 auto var(--s-3);display:flex;align-items:center;justify-content:center;font-size:28px;">
${org.logo_url ? `<img src="${org.logo_url}" style="width:100%;height:100%;border-radius:var(--r-md);object-fit:cover;">` : '🏛️'} ${org.logo_url ? `<img src="${org.logo_url}" style="width:100%;height:100%;border-radius:var(--r-md);object-fit:cover;">` : emoji('building', '🏛️', 28)}
</div> </div>
<div style="font-size:16px;font-weight:700;">${org.name_ar || org.name}</div> <div style="font-size:16px;font-weight:700;">${org.name_ar || org.name}</div>
<div style="font-size:13px;color:var(--text-secondary);margin-top:var(--s-2);">${org.description || ''}</div> <div style="font-size:13px;color:var(--text-secondary);margin-top:var(--s-2);">${org.description || ''}</div>
...@@ -56,7 +57,7 @@ function renderOrg(el, org) { ...@@ -56,7 +57,7 @@ function renderOrg(el, org) {
<div style="font-size:15px;font-weight:600;margin-bottom:var(--s-3);">الأعضاء (${org.members.length})</div> <div style="font-size:15px;font-weight:600;margin-bottom:var(--s-3);">الأعضاء (${org.members.length})</div>
${org.members.slice(0, 10).map(m => ` ${org.members.slice(0, 10).map(m => `
<div style="display:flex;align-items:center;gap:var(--s-2);padding:var(--s-1) 0;"> <div style="display:flex;align-items:center;gap:var(--s-2);padding:var(--s-1) 0;">
<div style="width:24px;height:24px;border-radius:50%;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;font-size:12px;">👤</div> <div style="width:24px;height:24px;border-radius:50%;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;font-size:12px;">${emoji('person', '👤', 12)}</div>
<span style="font-size:13px;">${m.user_id?.substring(0, 8) || 'Member'}</span> <span style="font-size:13px;">${m.user_id?.substring(0, 8) || 'Member'}</span>
<span style="font-size:10px;color:var(--text-muted);margin-right:auto;">${m.role}</span> <span style="font-size:10px;color:var(--text-muted);margin-right:auto;">${m.role}</span>
</div> </div>
......
...@@ -3,7 +3,7 @@ import * as scene from '../../../core/scene.js'; ...@@ -3,7 +3,7 @@ import * as scene from '../../../core/scene.js';
import * as store from '../../../core/store.js'; import * as store from '../../../core/store.js';
import * as audio from '../../../core/audio.js'; import * as audio from '../../../core/audio.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { assetImg } from '../../../core/theme.js'; import { assetImg, emoji } from '../../../core/theme.js';
import * as juice from '../../../core/juice.js'; import * as juice from '../../../core/juice.js';
const games = [ const games = [
...@@ -28,7 +28,7 @@ export function mountTable(el) { ...@@ -28,7 +28,7 @@ export function mountTable(el) {
<!-- Quick actions row --> <!-- Quick actions row -->
<div id="daily-widget" style="display:flex;gap:8px;width:100%;max-width:340px;margin-bottom:16px;"> <div id="daily-widget" style="display:flex;gap:8px;width:100%;max-width:340px;margin-bottom:16px;">
<button class="quick-btn" id="btn-challenges"> <button class="quick-btn" id="btn-challenges">
<span class="qb-icon" style="background:linear-gradient(135deg,#1e40af,#3b82f6);"></span> <span class="qb-icon" style="background:linear-gradient(135deg,#1e40af,#3b82f6);">${emoji('lightning', '⚡', 20)}</span>
<span class="qb-label">تحديات</span> <span class="qb-label">تحديات</span>
</button> </button>
<button class="quick-btn" id="btn-battlepass"> <button class="quick-btn" id="btn-battlepass">
...@@ -36,7 +36,7 @@ export function mountTable(el) { ...@@ -36,7 +36,7 @@ export function mountTable(el) {
<span class="qb-label">الموسم</span> <span class="qb-label">الموسم</span>
</button> </button>
<button class="quick-btn breathe-glow" id="btn-daily-reward"> <button class="quick-btn breathe-glow" id="btn-daily-reward">
<span class="qb-icon" style="background:linear-gradient(135deg,#92400e,#e4ac38);">🎁</span> <span class="qb-icon" style="background:linear-gradient(135deg,#92400e,#e4ac38);">${emoji('gift', '🎁', 20)}</span>
<span class="qb-label">هدية</span> <span class="qb-label">هدية</span>
</button> </button>
</div> </div>
...@@ -262,7 +262,7 @@ function showGameMenu(menu, game) { ...@@ -262,7 +262,7 @@ function showGameMenu(menu, game) {
<!-- Main play buttons --> <!-- Main play buttons -->
<button class="gm-btn" id="btn-single"> <button class="gm-btn" id="btn-single">
<div class="gm-btn-icon" style="background:${game.gradient};">🎮</div> <div class="gm-btn-icon" style="background:${game.gradient};">${emoji('gamepad', '🎮', 26)}</div>
<div class="gm-btn-body"> <div class="gm-btn-body">
<div class="gm-btn-title">لاعب واحد</div> <div class="gm-btn-title">لاعب واحد</div>
<div class="gm-btn-sub">العب ضد الكمبيوتر</div> <div class="gm-btn-sub">العب ضد الكمبيوتر</div>
...@@ -271,7 +271,7 @@ function showGameMenu(menu, game) { ...@@ -271,7 +271,7 @@ function showGameMenu(menu, game) {
</button> </button>
<button class="gm-btn" id="btn-multi"> <button class="gm-btn" id="btn-multi">
<div class="gm-btn-icon" style="background:linear-gradient(135deg,#dc2626,#f97316);">⚔️</div> <div class="gm-btn-icon" style="background:linear-gradient(135deg,#dc2626,#f97316);">${emoji('swords', '⚔️', 26)}</div>
<div class="gm-btn-body"> <div class="gm-btn-body">
<div class="gm-btn-title">أونلاين</div> <div class="gm-btn-title">أونلاين</div>
<div class="gm-btn-sub">نافس لاعبين حقيقيين</div> <div class="gm-btn-sub">نافس لاعبين حقيقيين</div>
...@@ -282,15 +282,15 @@ function showGameMenu(menu, game) { ...@@ -282,15 +282,15 @@ function showGameMenu(menu, game) {
<!-- Secondary actions --> <!-- Secondary actions -->
<div style="display:grid;grid-template-columns:${game.key === 'chess' ? '1fr 1fr 1fr' : '1fr 1fr'};gap:8px;margin-top:14px;"> <div style="display:grid;grid-template-columns:${game.key === 'chess' ? '1fr 1fr 1fr' : '1fr 1fr'};gap:8px;margin-top:14px;">
<button class="gm-chip" id="btn-leaderboard"> <button class="gm-chip" id="btn-leaderboard">
<span style="font-size:18px;">🏆</span> <span style="font-size:18px;">${emoji('tournament_cup', '🏆', 18)}</span>
<span>الترتيب</span> <span>الترتيب</span>
</button> </button>
<button class="gm-chip" id="btn-history"> <button class="gm-chip" id="btn-history">
<span style="font-size:18px;">📋</span> <span style="font-size:18px;">${emoji('clipboard', '📋', 18)}</span>
<span>مبارياتي</span> <span>مبارياتي</span>
</button> </button>
${game.key === 'chess' ? `<button class="gm-chip" id="btn-puzzles"> ${game.key === 'chess' ? `<button class="gm-chip" id="btn-puzzles">
<span style="font-size:18px;">🧩</span> <span style="font-size:18px;">${emoji('puzzle', '🧩', 18)}</span>
<span>أحجيات</span> <span>أحجيات</span>
</button>` : ''} </button>` : ''}
</div> </div>
......
...@@ -2,6 +2,7 @@ import * as store from '../../../core/store.js'; ...@@ -2,6 +2,7 @@ import * as store from '../../../core/store.js';
import * as scene from '../../../core/scene.js'; import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js'; import * as audio from '../../../core/audio.js';
import { t, setLang } from '../../../core/i18n.js'; import { t, setLang } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export function mountSettings(el) { export function mountSettings(el) {
const lang = store.get('language') || 'ar'; const lang = store.get('language') || 'ar';
...@@ -17,7 +18,7 @@ export function mountSettings(el) { ...@@ -17,7 +18,7 @@ export function mountSettings(el) {
<div class="card" style="display:flex;flex-direction:column;gap:var(--s-4);"> <div class="card" style="display:flex;flex-direction:column;gap:var(--s-4);">
<div style="display:flex;justify-content:space-between;align-items:center;"> <div style="display:flex;justify-content:space-between;align-items:center;">
<span>الصوت</span> <span>الصوت</span>
<button class="btn btn-secondary" id="toggle-audio" style="min-height:36px;padding:var(--s-1) var(--s-3);">${audioOn ? '🔊 مفعل' : '🔇 معطل'}</button> <button class="btn btn-secondary" id="toggle-audio" style="min-height:36px;padding:var(--s-1) var(--s-3);">${audioOn ? emoji('speaker_on', '🔊', 16) + ' مفعل' : emoji('speaker_off', '🔇', 16) + ' معطل'}</button>
</div> </div>
<div style="display:flex;justify-content:space-between;align-items:center;"> <div style="display:flex;justify-content:space-between;align-items:center;">
<span>اللغة</span> <span>اللغة</span>
......
...@@ -4,6 +4,7 @@ import * as audio from '../../../core/audio.js'; ...@@ -4,6 +4,7 @@ import * as audio from '../../../core/audio.js';
import * as bus from '../../../core/bus.js'; import * as bus from '../../../core/bus.js';
import * as net from '../../../core/net.js'; import * as net from '../../../core/net.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export async function mountView(el) { export async function mountView(el) {
// Always fetch fresh profile data // Always fetch fresh profile data
...@@ -18,7 +19,7 @@ export async function mountView(el) { ...@@ -18,7 +19,7 @@ export async function mountView(el) {
<!-- Player Card --> <!-- Player Card -->
<div class="card" style="text-align:center;padding:var(--s-6);"> <div class="card" style="text-align:center;padding:var(--s-6);">
<div style="width:80px;height:80px;border-radius:50%;background:var(--bg-elevated);margin:0 auto var(--s-3);display:flex;align-items:center;justify-content:center;font-size:36px;border:3px solid var(--gold);"> <div style="width:80px;height:80px;border-radius:50%;background:var(--bg-elevated);margin:0 auto var(--s-3);display:flex;align-items:center;justify-content:center;font-size:36px;border:3px solid var(--gold);">
${player.avatar_url ? `<img src="${player.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : '👤'} ${player.avatar_url ? `<img src="${player.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : emoji('person', '👤', 36)}
</div> </div>
<div style="font-size:18px;font-weight:700;">${player.display_name || player.username || 'Player'}</div> <div style="font-size:18px;font-weight:700;">${player.display_name || player.username || 'Player'}</div>
<div style="font-size:13px;color:var(--text-secondary);margin-top:2px;">Level ${player.level || 1}</div> <div style="font-size:13px;color:var(--text-secondary);margin-top:2px;">Level ${player.level || 1}</div>
......
...@@ -5,6 +5,7 @@ import * as bus from '../../../core/bus.js'; ...@@ -5,6 +5,7 @@ import * as bus from '../../../core/bus.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { ChessBoard } from '../../chess/canvas/board.js'; import { ChessBoard } from '../../chess/canvas/board.js';
import * as engine from '../../chess/logic/engine.js'; import * as engine from '../../chess/logic/engine.js';
import { emoji } from '../../../core/theme.js';
let board, puzzleData, moveIndex, solved; let board, puzzleData, moveIndex, solved;
...@@ -51,7 +52,7 @@ function setupPuzzle(el, puzzle) { ...@@ -51,7 +52,7 @@ function setupPuzzle(el, puzzle) {
engine.create(fen); engine.create(fen);
const ratingEl = el.querySelector('#puzzle-rating'); const ratingEl = el.querySelector('#puzzle-rating');
ratingEl.textContent = `⚡ ${puzzle.rating || '?'}`; ratingEl.innerHTML = `${emoji('lightning', '⚡', 13)} ${puzzle.rating || '?'}`;
const playerColor = engine.turn() === 'w' ? 'b' : 'w'; const playerColor = engine.turn() === 'w' ? 'b' : 'w';
...@@ -123,7 +124,7 @@ function onSolved(el) { ...@@ -123,7 +124,7 @@ function onSolved(el) {
board.interactive = false; board.interactive = false;
audio.play('win', 'reward'); audio.play('win', 'reward');
const status = el.querySelector('#puzzle-status'); const status = el.querySelector('#puzzle-status');
status.textContent = '✅ أحسنت! حل صحيح'; status.innerHTML = `${emoji('checkmark', '✅', 15)} أحسنت! حل صحيح`;
status.style.color = 'var(--win)'; status.style.color = 'var(--win)';
setTimeout(() => { setTimeout(() => {
...@@ -136,7 +137,7 @@ function onFailed(el) { ...@@ -136,7 +137,7 @@ function onFailed(el) {
board.interactive = false; board.interactive = false;
audio.play('lose', 'game'); audio.play('lose', 'game');
const status = el.querySelector('#puzzle-status'); const status = el.querySelector('#puzzle-status');
status.textContent = '❌ حل خاطئ'; status.innerHTML = `${emoji('cross', '❌', 15)} حل خاطئ`;
status.style.color = 'var(--loss)'; status.style.color = 'var(--loss)';
setTimeout(() => { setTimeout(() => {
......
...@@ -2,6 +2,7 @@ import * as net from '../../../core/net.js'; ...@@ -2,6 +2,7 @@ import * as net from '../../../core/net.js';
import * as scene from '../../../core/scene.js'; import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js'; import * as audio from '../../../core/audio.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export async function mountLeaderboard(el) { export async function mountLeaderboard(el) {
el.innerHTML = ` el.innerHTML = `
...@@ -61,13 +62,13 @@ function renderList(container, players) { ...@@ -61,13 +62,13 @@ function renderList(container, players) {
return; return;
} }
const medals = ['🥇', '🥈', '🥉']; const medals = [emoji('medal_gold', '🥇', 18), emoji('medal_silver', '🥈', 18), emoji('medal_bronze', '🥉', 18)];
container.innerHTML = players.map((p, i) => ` container.innerHTML = players.map((p, i) => `
<div class="card" style="display:flex;align-items:center;gap:var(--s-3);padding:var(--s-3);margin-bottom:var(--s-2);"> <div class="card" style="display:flex;align-items:center;gap:var(--s-3);padding:var(--s-3);margin-bottom:var(--s-2);">
<div style="width:28px;text-align:center;font-size:${i < 3 ? '18px' : '13px'};font-weight:700;color:${i < 3 ? 'var(--gold)' : 'var(--text-muted)'};"> <div style="width:28px;text-align:center;font-size:${i < 3 ? '18px' : '13px'};font-weight:700;color:${i < 3 ? 'var(--gold)' : 'var(--text-muted)'};">
${i < 3 ? medals[i] : i + 1} ${i < 3 ? medals[i] : i + 1}
</div> </div>
<div style="width:32px;height:32px;border-radius:50%;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;font-size:14px;">👤</div> <div style="width:32px;height:32px;border-radius:50%;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;font-size:14px;">${emoji('person', '👤', 14)}</div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:600;">${p.display_name || p.username || 'Player'}</div> <div style="font-size:13px;font-weight:600;">${p.display_name || p.username || 'Player'}</div>
</div> </div>
......
...@@ -2,6 +2,7 @@ import * as scene from '../../../core/scene.js'; ...@@ -2,6 +2,7 @@ import * as scene from '../../../core/scene.js';
import * as net from '../../../core/net.js'; import * as net from '../../../core/net.js';
import * as audio from '../../../core/audio.js'; import * as audio from '../../../core/audio.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
let activeTab = 'info'; let activeTab = 'info';
...@@ -100,7 +101,7 @@ async function loadInfo(content, tournamentId, el) { ...@@ -100,7 +101,7 @@ async function loadInfo(content, tournamentId, el) {
${data.prize_pool_coins ? `<div style="background:#1a1a2e;border-radius:10px;padding:14px;text-align:center;margin-bottom:12px;"> ${data.prize_pool_coins ? `<div style="background:#1a1a2e;border-radius:10px;padding:14px;text-align:center;margin-bottom:12px;">
<div style="font-size:12px;color:#64748b;margin-bottom:4px;">الجوائز</div> <div style="font-size:12px;color:#64748b;margin-bottom:4px;">الجوائز</div>
<div style="font-size:20px;font-weight:800;color:#E4AC38;">${data.prize_pool_coins} 🪙</div> <div style="font-size:20px;font-weight:800;color:#E4AC38;">${data.prize_pool_coins} ${emoji('coin', '🪙', 20)}</div>
</div>` : ''} </div>` : ''}
${data.starts_at ? `<div style="text-align:center;color:#94a3b8;font-size:12px;margin-bottom:16px;"> ${data.starts_at ? `<div style="text-align:center;color:#94a3b8;font-size:12px;margin-bottom:16px;">
...@@ -108,7 +109,7 @@ async function loadInfo(content, tournamentId, el) { ...@@ -108,7 +109,7 @@ async function loadInfo(content, tournamentId, el) {
</div>` : ''} </div>` : ''}
${data.status === 'registration' ? ` ${data.status === 'registration' ? `
<button class="btn btn-primary w-full" id="register-btn" style="font-size:16px;padding:16px;">⚔️ سجّل الآن</button> <button class="btn btn-primary w-full" id="register-btn" style="font-size:16px;padding:16px;">${emoji('swords', '⚔️', 16)} سجّل الآن</button>
` : ''} ` : ''}
`; `;
......
...@@ -3,6 +3,7 @@ import * as scene from '../../../core/scene.js'; ...@@ -3,6 +3,7 @@ import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js'; import * as audio from '../../../core/audio.js';
import * as store from '../../../core/store.js'; import * as store from '../../../core/store.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export async function mountTournaments(el) { export async function mountTournaments(el) {
el.innerHTML = ` el.innerHTML = `
...@@ -47,9 +48,9 @@ function renderTournaments(el, tournaments) { ...@@ -47,9 +48,9 @@ function renderTournaments(el, tournaments) {
<span style="font-size:10px;padding:3px 8px;border-radius:9999px;background:${getStatusColor(tour.status)};color:white;font-weight:600;">${getStatusLabel(tour.status)}</span> <span style="font-size:10px;padding:3px 8px;border-radius:9999px;background:${getStatusColor(tour.status)};color:white;font-weight:600;">${getStatusLabel(tour.status)}</span>
</div> </div>
<div style="display:flex;gap:16px;margin-top:12px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.05);"> <div style="display:flex;gap:16px;margin-top:12px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.05);">
<span style="font-size:11px;color:#94a3b8;">👥 ${tour.player_count || 0}/${tour.max_players || 32}</span> <span style="font-size:11px;color:#94a3b8;">${emoji('people', '👥', 11)} ${tour.player_count || 0}/${tour.max_players || 32}</span>
<span style="font-size:11px;color:#E4AC38;">🏆 ${tour.prize_pool_coins ? tour.prize_pool_coins + ' عملة' : 'N/A'}</span> <span style="font-size:11px;color:#E4AC38;">${emoji('tournament_cup', '🏆', 11)} ${tour.prize_pool_coins ? tour.prize_pool_coins + ' عملة' : 'N/A'}</span>
${tour.entry_fee_coins ? `<span style="font-size:11px;color:#F87171;">💰 ${tour.entry_fee_coins}</span>` : ''} ${tour.entry_fee_coins ? `<span style="font-size:11px;color:#F87171;">${emoji('money_bag', '💰', 11)} ${tour.entry_fee_coins}</span>` : ''}
</div> </div>
${tour.status === 'registration' ? `<button class="btn btn-primary w-full register-btn" data-tid="${tour.id}" style="margin-top:12px;font-size:13px;">سجّل الآن</button>` : ''} ${tour.status === 'registration' ? `<button class="btn btn-primary w-full register-btn" data-tid="${tour.id}" style="margin-top:12px;font-size:13px;">سجّل الآن</button>` : ''}
</div> </div>
......
...@@ -11,12 +11,12 @@ export async function mountChallenges(el) { ...@@ -11,12 +11,12 @@ export async function mountChallenges(el) {
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;"> <div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<div style="display:flex;align-items:center;gap:12px;"> <div style="display:flex;align-items:center;gap:12px;">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button>
<h2 style="font-size:18px;font-weight:800;color:#f8fafc;flex:1;"> التحديات اليومية</h2> <h2 style="font-size:18px;font-weight:800;color:#f8fafc;flex:1;">${emoji('lightning', '⚡', 18)} التحديات اليومية</h2>
<div id="streak-badge" style="background:linear-gradient(135deg,#E4AC38,#FFCC66);color:#1a1a1a;font-size:12px;font-weight:800;padding:5px 12px;border-radius:99px;"></div> <div id="streak-badge" style="background:linear-gradient(135deg,#E4AC38,#FFCC66);color:#1a1a1a;font-size:12px;font-weight:800;padding:5px 12px;border-radius:99px;"></div>
</div> </div>
<div id="challenges-list"></div> <div id="challenges-list"></div>
<div id="all-done" style="display:none;text-align:center;padding:20px;"> <div id="all-done" style="display:none;text-align:center;padding:20px;">
<div style="font-size:40px;margin-bottom:8px;"></div> <div style="font-size:40px;margin-bottom:8px;">${emoji('checkmark', '✅', 40)}</div>
<div style="font-size:15px;font-weight:700;color:#34D399;">أنجزت كل التحديات اليوم!</div> <div style="font-size:15px;font-weight:700;color:#34D399;">أنجزت كل التحديات اليوم!</div>
</div> </div>
</div> </div>
......
...@@ -56,7 +56,7 @@ export async function mountDaily(el) { ...@@ -56,7 +56,7 @@ export async function mountDaily(el) {
<!-- Today's reward --> <!-- Today's reward -->
<div style="text-align:center;margin-top:8px;"> <div style="text-align:center;margin-top:8px;">
${alreadyClaimed ? ` ${alreadyClaimed ? `
<div style="font-size:48px;margin-bottom:8px;"></div> <div style="font-size:48px;margin-bottom:8px;">${emoji('checkmark', '', 48)}</div>
<div style="font-size:16px;font-weight:700;color:#34D399;">تم استلام مكافأة اليوم</div> <div style="font-size:16px;font-weight:700;color:#34D399;">تم استلام مكافأة اليوم</div>
<div style="font-size:13px;color:#64748b;margin-top:4px;">عد غداً لمكافأة أكبر!</div> <div style="font-size:13px;color:#64748b;margin-top:4px;">عد غداً لمكافأة أكبر!</div>
` : ` ` : `
......
import { emoji } from '../../../core/theme.js';
// Rank tier system // Rank tier system
export const TIERS = [ export const TIERS = [
{ name: 'برونزي', nameEn: 'Bronze', min: 0, max: 999, color: '#CD7F32', icon: '🥉' }, { name: 'برونزي', nameEn: 'Bronze', min: 0, max: 999, color: '#CD7F32', get icon() { return emoji('medal_bronze', '🥉', 16); } },
{ name: 'فضي', nameEn: 'Silver', min: 1000, max: 1199, color: '#C0C0C0', icon: '🥈' }, { name: 'فضي', nameEn: 'Silver', min: 1000, max: 1199, color: '#C0C0C0', get icon() { return emoji('medal_silver', '🥈', 16); } },
{ name: 'ذهبي', nameEn: 'Gold', min: 1200, max: 1399, color: '#FFD700', icon: '🥇' }, { name: 'ذهبي', nameEn: 'Gold', min: 1200, max: 1399, color: '#FFD700', get icon() { return emoji('medal_gold', '🥇', 16); } },
{ name: 'بلاتيني', nameEn: 'Platinum', min: 1400, max: 1599, color: '#00CED1', icon: '💎' }, { name: 'بلاتيني', nameEn: 'Platinum', min: 1400, max: 1599, color: '#00CED1', get icon() { return emoji('gem', '💎', 16); } },
{ name: 'ماسي', nameEn: 'Diamond', min: 1600, max: 1799, color: '#B9F2FF', icon: '💠' }, { name: 'ماسي', nameEn: 'Diamond', min: 1600, max: 1799, color: '#B9F2FF', icon: '💠' },
{ name: 'أسطوري', nameEn: 'Master', min: 1800, max: 2000, color: '#FF4500', icon: '👑' }, { name: 'أسطوري', nameEn: 'Master', min: 1800, max: 2000, color: '#FF4500', icon: '👑' },
{ name: 'جراند ماستر', nameEn: 'Grandmaster', min: 2000, max: 9999, color: '#8B008B', icon: '🏅' }, { name: 'جراند ماستر', nameEn: 'Grandmaster', min: 2000, max: 9999, color: '#8B008B', icon: '🏅' },
......
...@@ -38,7 +38,7 @@ function renderItems(el, items) { ...@@ -38,7 +38,7 @@ function renderItems(el, items) {
grid.innerHTML = items.map(item => ` grid.innerHTML = items.map(item => `
<div class="card shop-item" data-id="${item.id}" style="cursor:pointer;padding:var(--s-3);border-color:${RARITY_COLORS[item.rarity] || 'var(--border)'}33;"> <div class="card shop-item" data-id="${item.id}" style="cursor:pointer;padding:var(--s-3);border-color:${RARITY_COLORS[item.rarity] || 'var(--border)'}33;">
<div style="height:60px;background:var(--bg-elevated);border-radius:var(--r-sm);margin-bottom:var(--s-2);display:flex;align-items:center;justify-content:center;font-size:24px;"> <div style="height:60px;background:var(--bg-elevated);border-radius:var(--r-sm);margin-bottom:var(--s-2);display:flex;align-items:center;justify-content:center;font-size:24px;">
🎨 ${emoji('art', '🎨', 24)}
</div> </div>
<div style="font-size:13px;font-weight:600;margin-bottom:2px;">${item.name || item.id}</div> <div style="font-size:13px;font-weight:600;margin-bottom:2px;">${item.name || item.id}</div>
<div style="font-size:10px;color:${RARITY_COLORS[item.rarity] || 'var(--text-muted)'};margin-bottom:var(--s-1);">${item.rarity || 'common'}</div> <div style="font-size:10px;color:${RARITY_COLORS[item.rarity] || 'var(--text-muted)'};margin-bottom:var(--s-1);">${item.rarity || 'common'}</div>
...@@ -88,7 +88,7 @@ async function purchasePrompt(el, item) { ...@@ -88,7 +88,7 @@ async function purchasePrompt(el, item) {
await net.post('shop.php', { action: 'purchase', cosmetic_id: item.id }); await net.post('shop.php', { action: 'purchase', cosmetic_id: item.id });
audio.play('coin', 'reward'); audio.play('coin', 'reward');
store.set('player.coins', (player.coins || 0) - (item.price_coins || 0)); store.set('player.coins', (player.coins || 0) - (item.price_coins || 0));
overlay.innerHTML = `<div class="card" style="padding:var(--s-6);text-align:center;"><div style="font-size:36px;margin-bottom:var(--s-2);"></div><div>تم الشراء!</div></div>`; overlay.innerHTML = `<div class="card" style="padding:var(--s-6);text-align:center;"><div style="font-size:36px;margin-bottom:var(--s-2);">${emoji('checkmark', '✅', 36)}</div><div>تم الشراء!</div></div>`;
setTimeout(() => { overlay.classList.remove('active'); overlay.innerHTML = ''; }, 1500); setTimeout(() => { overlay.classList.remove('active'); overlay.innerHTML = ''; }, 1500);
} catch (e) { } catch (e) {
overlay.innerHTML = `<div class="card" style="padding:var(--s-6);text-align:center;color:var(--error);">${t('common.error')}</div>`; overlay.innerHTML = `<div class="card" style="padding:var(--s-6);text-align:center;color:var(--error);">${t('common.error')}</div>`;
......
import * as net from '../../../core/net.js'; import * as net from '../../../core/net.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export async function mountActivity(el) { export async function mountActivity(el) {
el.innerHTML = ` el.innerHTML = `
...@@ -42,7 +43,7 @@ function renderActivity(el, activities) { ...@@ -42,7 +43,7 @@ function renderActivity(el, activities) {
return ` return `
<div style="display:flex;gap:10px;padding:10px;background:#1a1a2e;border-radius:10px;"> <div style="display:flex;gap:10px;padding:10px;background:#1a1a2e;border-radius:10px;">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;"> <div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;">
${actor.avatar_url ? `<img src="${actor.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : '👤'} ${actor.avatar_url ? `<img src="${actor.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : emoji('person', '👤', 14)}
</div> </div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;color:#f8fafc;"><strong>${actor.display_name || actor.username || '?'}</strong> ${label}</div> <div style="font-size:13px;color:#f8fafc;"><strong>${actor.display_name || actor.username || '?'}</strong> ${label}</div>
......
...@@ -4,6 +4,7 @@ import * as audio from '../../../core/audio.js'; ...@@ -4,6 +4,7 @@ import * as audio from '../../../core/audio.js';
import * as bus from '../../../core/bus.js'; import * as bus from '../../../core/bus.js';
import * as juice from '../../../core/juice.js'; import * as juice from '../../../core/juice.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
let activeTab = 'friends'; let activeTab = 'friends';
...@@ -81,10 +82,10 @@ async function loadFriends(content) { ...@@ -81,10 +82,10 @@ async function loadFriends(content) {
if (friends.length === 0) { if (friends.length === 0) {
content.innerHTML = ` content.innerHTML = `
<div style="text-align:center;padding:40px 20px;"> <div style="text-align:center;padding:40px 20px;">
<div style="font-size:48px;margin-bottom:12px;opacity:0.5;">👥</div> <div style="font-size:48px;margin-bottom:12px;opacity:0.5;">${emoji('people', '👥', 48)}</div>
<div style="font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:6px;">لا يوجد أصدقاء بعد</div> <div style="font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:6px;">لا يوجد أصدقاء بعد</div>
<div style="font-size:12px;color:#64748b;margin-bottom:16px;">ابحث عن لاعبين وأرسل لهم طلب صداقة</div> <div style="font-size:12px;color:#64748b;margin-bottom:16px;">ابحث عن لاعبين وأرسل لهم طلب صداقة</div>
<button class="btn btn-primary" id="empty-search" style="font-size:13px;padding:10px 24px;">🔍 ابحث عن لاعبين</button> <button class="btn btn-primary" id="empty-search" style="font-size:13px;padding:10px 24px;">${emoji('search_icon', '🔍', 13)} ابحث عن لاعبين</button>
</div>`; </div>`;
content.querySelector('#empty-search')?.addEventListener('click', () => showSearch(content.closest('[style*="height:100%"]') || content.parentElement)); content.querySelector('#empty-search')?.addEventListener('click', () => showSearch(content.closest('[style*="height:100%"]') || content.parentElement));
return; return;
...@@ -109,7 +110,7 @@ async function loadPending(content) { ...@@ -109,7 +110,7 @@ async function loadPending(content) {
content.innerHTML = pending.map(p => ` content.innerHTML = pending.map(p => `
<div class="friend-card"> <div class="friend-card">
<div class="friend-avatar">👤</div> <div class="friend-avatar">${emoji('person', '👤', 16)}</div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:600;color:#f8fafc;">${p.requester_id?.substring(0, 8) || 'Player'}</div> <div style="font-size:13px;font-weight:600;color:#f8fafc;">${p.requester_id?.substring(0, 8) || 'Player'}</div>
<div style="font-size:11px;color:#64748b;">أرسل لك طلب صداقة</div> <div style="font-size:11px;color:#64748b;">أرسل لك طلب صداقة</div>
...@@ -164,7 +165,7 @@ function renderFriendCard(f, context) { ...@@ -164,7 +165,7 @@ function renderFriendCard(f, context) {
return ` return `
<div class="friend-card" data-uid="${f.id}"> <div class="friend-card" data-uid="${f.id}">
<div class="friend-avatar"> <div class="friend-avatar">
${f.avatar_url ? `<img src="${f.avatar_url}">` : '👤'} ${f.avatar_url ? `<img src="${f.avatar_url}">` : emoji('person', '👤', 16)}
${f.is_online ? '<div class="online-dot"></div>' : ''} ${f.is_online ? '<div class="online-dot"></div>' : ''}
</div> </div>
<div style="flex:1;"> <div style="flex:1;">
...@@ -172,8 +173,8 @@ function renderFriendCard(f, context) { ...@@ -172,8 +173,8 @@ function renderFriendCard(f, context) {
<div style="font-size:11px;color:${f.is_online ? '#34D399' : '#64748b'};">${f.is_online ? 'متصل الآن' : 'غير متصل'}</div> <div style="font-size:11px;color:${f.is_online ? '#34D399' : '#64748b'};">${f.is_online ? 'متصل الآن' : 'غير متصل'}</div>
</div> </div>
<div class="friend-actions"> <div class="friend-actions">
${f.is_online ? `<div class="friend-action" data-invite="${f.id}" title="دعوة للعب">⚔️</div>` : ''} ${f.is_online ? `<div class="friend-action" data-invite="${f.id}" title="دعوة للعب">${emoji('challenge_swords', '⚔️', 16)}</div>` : ''}
<div class="friend-action" data-profile="${f.id}" title="الملف الشخصي">👤</div> <div class="friend-action" data-profile="${f.id}" title="الملف الشخصي">${emoji('person', '👤', 16)}</div>
<div class="friend-action" data-remove="${f.id}" title="إزالة" style="font-size:12px;">✕</div> <div class="friend-action" data-remove="${f.id}" title="إزالة" style="font-size:12px;">✕</div>
</div> </div>
</div> </div>
...@@ -234,7 +235,7 @@ function showSearch(el) { ...@@ -234,7 +235,7 @@ function showSearch(el) {
} }
results.innerHTML = players.map(p => ` results.innerHTML = players.map(p => `
<div class="friend-card"> <div class="friend-card">
<div class="friend-avatar">${p.avatar_url ? `<img src="${p.avatar_url}">` : '👤'}</div> <div class="friend-avatar">${p.avatar_url ? `<img src="${p.avatar_url}">` : emoji('person', '👤', 16)}</div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:600;color:#f8fafc;">${p.display_name || p.username}</div> <div style="font-size:13px;font-weight:600;color:#f8fafc;">${p.display_name || p.username}</div>
<div style="font-size:11px;color:#64748b;">Level ${p.level || 1}</div> <div style="font-size:11px;color:#64748b;">Level ${p.level || 1}</div>
...@@ -275,7 +276,7 @@ async function loadActivity(content) { ...@@ -275,7 +276,7 @@ async function loadActivity(content) {
const actor = a.actor || {}; const actor = a.actor || {};
const label = labels[a.action] || a.action; const label = labels[a.action] || a.action;
return `<div style="display:flex;gap:10px;padding:10px;background:#1a1a2e;border-radius:10px;margin-bottom:6px;"> return `<div style="display:flex;gap:10px;padding:10px;background:#1a1a2e;border-radius:10px;margin-bottom:6px;">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;">👤</div> <div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;">${emoji('person', '👤', 14)}</div>
<div style="flex:1;"><div style="font-size:13px;color:#f8fafc;"><strong>${actor.display_name || actor.username || "?"}</strong> ${label}</div></div> <div style="flex:1;"><div style="font-size:13px;color:#f8fafc;"><strong>${actor.display_name || actor.username || "?"}</strong> ${label}</div></div>
</div>`; </div>`;
}).join(""); }).join("");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment