552 lines
24 KiB
JavaScript
552 lines
24 KiB
JavaScript
// ==================== START MENU ====================
|
||
import { state } from '../core/state.js';
|
||
|
||
// Detect Telegram WebApp
|
||
const isTgWebApp = !!(window.Telegram && window.Telegram.WebApp && window.Telegram.WebApp.initData);
|
||
let tgUser = null;
|
||
let tgSaveLoaded = false;
|
||
|
||
if (isTgWebApp) {
|
||
try {
|
||
Telegram.WebApp.ready();
|
||
Telegram.WebApp.expand();
|
||
tgUser = Telegram.WebApp.initDataUnsafe?.user || null;
|
||
console.log('[TG] Mini App detected, user:', tgUser?.id, tgUser?.username);
|
||
// Set player name from TG
|
||
if (tgUser && tgUser.first_name) {
|
||
const tgName = tgUser.username || tgUser.first_name;
|
||
localStorage.setItem('minegrechka_playerName', tgName);
|
||
}
|
||
} catch (e) {
|
||
console.warn('[TG] WebApp init error:', e);
|
||
}
|
||
}
|
||
|
||
// Export TG info for other modules
|
||
export { isTgWebApp, tgUser };
|
||
|
||
// ==================== HELPERS ====================
|
||
|
||
function generateShortWorldCode() {
|
||
// Generate readable 6-char code: consonants + digits (easy to copy/share)
|
||
const c = 'bcdfghjkmnpqrstvwxyz';
|
||
const d = '23456789';
|
||
let code = '';
|
||
for (let i = 0; i < 3; i++) code += c[Math.floor(Math.random() * c.length)];
|
||
for (let i = 0; i < 3; i++) code += d[Math.floor(Math.random() * d.length)];
|
||
return code;
|
||
}
|
||
|
||
export function showToast(msg, duration) {
|
||
duration = duration || 1800;
|
||
const t = document.createElement('div');
|
||
t.textContent = msg;
|
||
t.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.85);color:#2ecc71;padding:10px 24px;border-radius:10px;font:bold 16px Arial,sans-serif;z-index:99999;pointer-events:none;transition:opacity 0.4s;';
|
||
document.body.appendChild(t);
|
||
setTimeout(() => { t.style.opacity = '0'; setTimeout(() => t.remove(), 400); }, duration);
|
||
}
|
||
|
||
function customAlert(msg) {
|
||
const overlay = document.createElement('div');
|
||
overlay.className = 'custom-modal-overlay';
|
||
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:100000;';
|
||
const box = document.createElement('div');
|
||
box.style.cssText = 'background:#C6C6C6;border:4px solid #000;padding:24px;max-width:360px;width:90%;box-shadow:4px 4px 0 #000;font-family:Courier New,monospace;';
|
||
const text = document.createElement('div');
|
||
text.textContent = msg;
|
||
text.style.marginBottom = '16px';
|
||
text.style.fontWeight = 'bold';
|
||
text.style.fontSize = '16px';
|
||
const btn = document.createElement('button');
|
||
btn.textContent = 'OK';
|
||
btn.style.cssText = 'padding:8px 24px;font-family:Courier New,monospace;font-weight:bold;cursor:pointer;border:3px solid #000;background:#5A8F3C;color:#fff;box-shadow:2px 2px 0 #000;';
|
||
btn.onclick = () => overlay.remove();
|
||
box.appendChild(text);
|
||
box.appendChild(btn);
|
||
overlay.appendChild(box);
|
||
document.body.appendChild(overlay);
|
||
}
|
||
|
||
// ==================== MAIN START MENU ====================
|
||
|
||
// Minecraft-style CSS for the start menu
|
||
const mcCSS = `
|
||
.mc-overlay { position:fixed;top:0;left:0;width:100%;height:100%;z-index:99999;
|
||
display:flex;align-items:center;justify-content:center;font-family:'Courier New',monospace;
|
||
image-rendering:pixelated; }
|
||
.mc-bg { position:absolute;inset:0;background:#000;opacity:0.75; }
|
||
.mc-dirt { position:absolute;bottom:0;left:0;right:0;height:40%;
|
||
background:repeating-linear-gradient(0deg,#8B6914 0px,#8B6914 2px,#7A5B12 2px,#7A5B12 4px);opacity:0.3; }
|
||
.mc-box { position:relative;z-index:1;background:#C6C6C6;border:4px solid #000;
|
||
padding:0;max-width:420px;width:92%;box-shadow:4px 4px 0 #000; }
|
||
.mc-titlebar { background:#1a1a2e;padding:12px 16px;text-align:center;
|
||
border-bottom:4px solid #000; }
|
||
.mc-title { font-size:24px;font-weight:bold;color:#FFAA00;text-shadow:2px 2px 0 #000;
|
||
letter-spacing:2px; }
|
||
.mc-subtitle { font-size:13px;color:#AAA;margin-top:4px; }
|
||
.mc-body { padding:16px;background:#C6C6C6; }
|
||
.mc-btn { display:block;width:100%;padding:10px 16px;margin-bottom:8px;
|
||
font-family:'Courier New',monospace;font-size:16px;font-weight:bold;
|
||
cursor:pointer;text-align:center;border:3px solid #000;
|
||
image-rendering:pixelated;box-shadow:3px 3px 0 #000;
|
||
transition:transform 0.05s,box-shadow 0.05s; }
|
||
.mc-btn:active { transform:translate(2px,2px);box-shadow:1px 1px 0 #000; }
|
||
.mc-btn-green { background:#5A8F3C;color:#E0E0E0;border-color:#3A5F1C;text-shadow:1px 1px 0 #222; }
|
||
.mc-btn-green:hover { background:#6DAF4E; }
|
||
.mc-btn-blue { background:#3C5F9E;color:#E0E0E0;border-color:#2A4070;text-shadow:1px 1px 0 #222; }
|
||
.mc-btn-blue:hover { background:#4B73BE; }
|
||
.mc-btn-purple { background:#7B3FA0;color:#E0E0E0;border-color:#5A2C78;text-shadow:1px 1px 0 #222; }
|
||
.mc-btn-purple:hover { background:#9555BE; }
|
||
.mc-btn-orange { background:#B86E00;color:#E0E0E0;border-color:#8A5200;text-shadow:1px 1px 0 #222; }
|
||
.mc-btn-orange:hover { background:#DA8500; }
|
||
.mc-btn-red { background:#9E2B2B;color:#E0E0E0;border-color:#6E1B1B;text-shadow:1px 1px 0 #222; }
|
||
.mc-btn-red:hover { background:#BE3B3B; }
|
||
.mc-btn-gray { background:#555;color:#AAA;border-color:#333;text-shadow:1px 1px 0 #111; }
|
||
.mc-btn-gray:hover { background:#666;color:#DDD; }
|
||
.mc-btn-outline { background:transparent;color:#555;border-color:#555;
|
||
text-shadow:none;box-shadow:2px 2px 0 #333; }
|
||
.mc-btn-outline:hover { background:#DDD;color:#222; }
|
||
.mc-input { width:100%;padding:8px 12px;font-family:'Courier New',monospace;
|
||
font-size:16px;background:#FFF;border:3px solid #000;color:#222;
|
||
box-shadow:inset 2px 2px 0 #999;margin:4px 0;box-sizing:border-box; }
|
||
.mc-input:focus { outline:none;border-color:#FFAA00; }
|
||
.mc-label { font-size:13px;color:#444;font-weight:bold;margin:8px 0 4px; }
|
||
.mc-divider { border:none;border-top:2px solid #999;margin:12px 0; }
|
||
.mc-friend-row { display:flex;justify-content:space-between;align-items:center;
|
||
padding:6px 8px;margin-bottom:4px;background:#E8E8E8;border:2px solid #999;
|
||
box-shadow:2px 2px 0 #999; }
|
||
.mc-friend-btn { padding:4px 10px;font-family:'Courier New',monospace;
|
||
font-size:13px;font-weight:bold;cursor:pointer;border:2px solid #000;
|
||
box-shadow:1px 1px 0 #000; }
|
||
.mc-friend-btn:active { transform:translate(1px,1px);box-shadow:none; }
|
||
.mc-friend-btn-green { background:#5A8F3C;color:#E0E0E0; }
|
||
.mc-friend-btn-red { background:#9E2B2B;color:#E0E0E0; }
|
||
.mc-checkbox-row { display:flex;align-items:center;gap:8px;font-size:13px;color:#444;cursor:pointer; }
|
||
.mc-checkbox { width:18px;height:18px;accent-color:#5A8F3C; }
|
||
`;
|
||
|
||
export function showStartMenu() {
|
||
return new Promise(async (resolve) => {
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const worldParam = urlParams.get('world');
|
||
const tgStartParam = (isTgWebApp && Telegram.WebApp.initDataUnsafe?.start_param)
|
||
? Telegram.WebApp.initDataUnsafe.start_param.trim()
|
||
: null;
|
||
|
||
// If world is in URL or TG start param — skip menu, auto-join
|
||
if ((worldParam && worldParam.trim() !== '') || tgStartParam) {
|
||
const worldId = (worldParam && worldParam.trim()) || tgStartParam;
|
||
console.log('[Menu] Joining world from URL/startParam:', worldId);
|
||
|
||
// Set player name from TG or localStorage or default
|
||
if (!state.playerName) {
|
||
if (isTgWebApp && tgUser) {
|
||
state.playerName = tgUser.username || tgUser.first_name || 'Игрок';
|
||
} else {
|
||
state.playerName = localStorage.getItem('minegrechka_playerName') || 'Игрок';
|
||
}
|
||
localStorage.setItem('minegrechka_playerName', state.playerName);
|
||
}
|
||
|
||
resolve(worldId);
|
||
return;
|
||
}
|
||
|
||
// Inject CSS
|
||
const styleEl = document.createElement('style');
|
||
styleEl.textContent = mcCSS;
|
||
document.head.appendChild(styleEl);
|
||
|
||
// Build overlay
|
||
const overlay = document.createElement('div');
|
||
overlay.className = 'mc-overlay';
|
||
overlay.id = 'startMenu';
|
||
overlay.innerHTML = '<div class="mc-bg"></div><div class="mc-dirt"></div>';
|
||
|
||
const box = document.createElement('div');
|
||
box.className = 'mc-box';
|
||
|
||
// Title bar
|
||
const titleBar = document.createElement('div');
|
||
titleBar.className = 'mc-titlebar';
|
||
titleBar.innerHTML = '<div class="mc-title">⛏️ GrechkaCraft</div>' +
|
||
'<div class="mc-subtitle">' + (isTgWebApp ? ('Привет, ' + (tgUser?.first_name || 'Игрок') + '!') : 'Voxel-мир в браузере') + '</div>';
|
||
box.appendChild(titleBar);
|
||
|
||
const body = document.createElement('div');
|
||
body.className = 'mc-body';
|
||
|
||
// Mutable worldId holder
|
||
let selectedWorldId = null;
|
||
|
||
// === Buttons ===
|
||
|
||
// 🌍 New World
|
||
const btnNew = document.createElement('button');
|
||
btnNew.className = 'mc-btn mc-btn-green';
|
||
btnNew.textContent = '🌍 Новый мир';
|
||
btnNew.onclick = () => {
|
||
selectedWorldId = generateShortWorldCode();
|
||
overlay.remove();
|
||
styleEl.remove();
|
||
resolve(selectedWorldId);
|
||
};
|
||
body.appendChild(btnNew);
|
||
|
||
// 🔗 Join by code
|
||
const btnJoin = document.createElement('button');
|
||
btnJoin.className = 'mc-btn mc-btn-blue';
|
||
btnJoin.textContent = '🔗 Вступить по коду';
|
||
btnJoin.onclick = () => {
|
||
body.innerHTML = '';
|
||
const lbl = document.createElement('div');
|
||
lbl.className = 'mc-label';
|
||
lbl.textContent = 'Код мира:';
|
||
body.appendChild(lbl);
|
||
const codeInput = document.createElement('input');
|
||
codeInput.className = 'mc-input';
|
||
codeInput.type = 'text';
|
||
codeInput.placeholder = 'bkh237';
|
||
codeInput.maxLength = 12;
|
||
body.appendChild(codeInput);
|
||
const btnGo = document.createElement('button');
|
||
btnGo.className = 'mc-btn mc-btn-blue';
|
||
btnGo.textContent = 'Войти →';
|
||
btnGo.onclick = () => {
|
||
const code = codeInput.value.trim().toLowerCase();
|
||
if (code.length >= 3) {
|
||
selectedWorldId = code;
|
||
overlay.remove();
|
||
styleEl.remove();
|
||
resolve(selectedWorldId);
|
||
}
|
||
};
|
||
body.appendChild(btnGo);
|
||
const btnBack = document.createElement('button');
|
||
btnBack.className = 'mc-btn mc-btn-gray';
|
||
btnBack.textContent = '← Назад';
|
||
btnBack.onclick = () => { overlay.remove(); styleEl.remove(); showStartMenu().then(resolve); };
|
||
body.appendChild(btnBack);
|
||
codeInput.focus();
|
||
codeInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') btnGo.click(); });
|
||
};
|
||
body.appendChild(btnJoin);
|
||
|
||
// 💾 Continue saved game
|
||
const hasLocalSave = !!localStorage.getItem(state.SAVE_KEY || 'minegrechka_save');
|
||
if (isTgWebApp || hasLocalSave) {
|
||
const btnHistory = document.createElement('button');
|
||
btnHistory.className = 'mc-btn mc-btn-purple';
|
||
btnHistory.textContent = '💾 Продолжить игру';
|
||
btnHistory.onclick = async () => {
|
||
if (isTgWebApp && tgUser) {
|
||
try {
|
||
const resp = await fetch(state.SERVER_URL + '/api/tg/save?tg_id=' + tgUser.id);
|
||
const data = await resp.json();
|
||
if (data.ok && data.world_id) {
|
||
selectedWorldId = data.world_id;
|
||
tgSaveLoaded = true;
|
||
overlay.remove();
|
||
styleEl.remove();
|
||
resolve(selectedWorldId);
|
||
return;
|
||
}
|
||
} catch (e) { console.warn('[TG] Load save failed:', e); }
|
||
}
|
||
if (hasLocalSave) {
|
||
// Continue with a new world code (the save will be loaded separately by initDB/loadGame)
|
||
selectedWorldId = generateShortWorldCode();
|
||
overlay.remove();
|
||
styleEl.remove();
|
||
resolve(selectedWorldId);
|
||
return;
|
||
}
|
||
customAlert('Сохранений не найдено');
|
||
};
|
||
body.appendChild(btnHistory);
|
||
}
|
||
|
||
// === TG-only buttons ===
|
||
if (isTgWebApp && window.Telegram && Telegram.WebApp.switchInlineQuery) {
|
||
// 📤 Invite friends
|
||
const btnInvite = document.createElement('button');
|
||
btnInvite.className = 'mc-btn mc-btn-outline';
|
||
btnInvite.style.marginTop = '16px';
|
||
btnInvite.textContent = '📤 Пригласить друзей';
|
||
btnInvite.onclick = () => {
|
||
const code = selectedWorldId || generateShortWorldCode();
|
||
const url = 'https://t.me/' + state.TELEGRAM_BOT_USERNAME + '/app?startapp=' + encodeURIComponent(code);
|
||
if (Telegram.WebApp && Telegram.WebApp.openTelegramLink) {
|
||
Telegram.WebApp.openTelegramLink('https://t.me/share/url?url=' + encodeURIComponent(url) + '&text=' + encodeURIComponent('🏗️ Заходи в GrechkaCraft! Мир: ' + code));
|
||
} else {
|
||
navigator.clipboard.writeText(url).then(() => showToast('📋 Скопировано!')).catch(() => showToast(url));
|
||
}
|
||
};
|
||
body.appendChild(btnInvite);
|
||
|
||
// 👥 Friends
|
||
const btnFriends = document.createElement('button');
|
||
btnFriends.className = 'mc-btn mc-btn-orange';
|
||
btnFriends.style.marginTop = '8px';
|
||
btnFriends.textContent = '👥 Друзья';
|
||
btnFriends.onclick = async () => {
|
||
try {
|
||
const resp = await fetch(state.SERVER_URL + '/api/tg/friends?tg_id=' + tgUser.id);
|
||
const data = await resp.json();
|
||
body.innerHTML = '';
|
||
|
||
// Back button at top
|
||
const btnBackTop = document.createElement('button');
|
||
btnBackTop.className = 'mc-btn mc-btn-gray';
|
||
btnBackTop.textContent = '← Назад';
|
||
btnBackTop.style.marginBottom = '12px';
|
||
btnBackTop.onclick = () => { overlay.remove(); styleEl.remove(); showStartMenu().then(resolve); };
|
||
body.appendChild(btnBackTop);
|
||
|
||
// Nickname section
|
||
if (data.nickname) {
|
||
const nickDiv = document.createElement('div');
|
||
nickDiv.className = 'mc-friend-row';
|
||
nickDiv.innerHTML = '🔗 Твой ник: <b>' + data.nickname + '</b>';
|
||
nickDiv.style.background = '#D5E8C5';
|
||
body.appendChild(nickDiv);
|
||
} else {
|
||
const nickLabel = document.createElement('div');
|
||
nickLabel.className = 'mc-label';
|
||
nickLabel.style.color = '#9E2B2B';
|
||
nickLabel.textContent = '⚡ Задай ник, чтобы друзья могли тебя найти:';
|
||
body.appendChild(nickLabel);
|
||
const nickRow = document.createElement('div');
|
||
nickRow.style.display = 'flex';
|
||
nickRow.style.gap = '4px';
|
||
nickRow.style.marginBottom = '8px';
|
||
const nickInput = document.createElement('input');
|
||
nickInput.className = 'mc-input';
|
||
nickInput.style.flex = '1';
|
||
nickInput.type = 'text';
|
||
nickInput.maxLength = 16;
|
||
nickInput.placeholder = 'Твой ник';
|
||
const nickBtn = document.createElement('button');
|
||
nickBtn.className = 'mc-btn mc-btn-green';
|
||
nickBtn.style.width = 'auto';
|
||
nickBtn.style.marginBottom = '0';
|
||
nickBtn.style.padding = '8px 16px';
|
||
nickBtn.textContent = '✓';
|
||
nickBtn.onclick = async () => {
|
||
const n = nickInput.value.trim().toLowerCase();
|
||
if (!n || n.length < 2) { showToast('❌ Минимум 2 символа'); return; }
|
||
const check = await fetch(state.SERVER_URL + '/api/nick/check?nick=' + encodeURIComponent(n));
|
||
const cd = await check.json();
|
||
if (!cd.available) { showToast('❌ Ник занят'); return; }
|
||
const setResp = await fetch(state.SERVER_URL + '/api/nick/set', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ tg_id: tgUser.id, nickname: n })
|
||
});
|
||
const sd = await setResp.json();
|
||
if (sd.ok) { showToast('✅ Ник: ' + sd.nickname); btnFriends.onclick(); } else { showToast('❌ Ошибка'); }
|
||
};
|
||
nickRow.appendChild(nickInput);
|
||
nickRow.appendChild(nickBtn);
|
||
body.appendChild(nickRow);
|
||
}
|
||
|
||
// Friends list
|
||
if (data.friends && data.friends.length) {
|
||
const listLabel = document.createElement('div');
|
||
listLabel.className = 'mc-label';
|
||
listLabel.textContent = '👥 Друзья:';
|
||
body.appendChild(listLabel);
|
||
for (const f of data.friends) {
|
||
const fDiv = document.createElement('div');
|
||
fDiv.className = 'mc-friend-row';
|
||
const statusDot = f.online ? '🟢' : '⚫';
|
||
let info = statusDot + ' <b>' + f.nickname + '</b>';
|
||
if (f.online && f.world_id) info += ' — мир ' + f.world_id;
|
||
const fInfo = document.createElement('div');
|
||
fInfo.innerHTML = info;
|
||
fDiv.appendChild(fInfo);
|
||
if (f.online && f.world_id) {
|
||
const joinBtn = document.createElement('button');
|
||
joinBtn.className = 'mc-friend-btn mc-friend-btn-green';
|
||
joinBtn.textContent = '🎮 Вступить';
|
||
joinBtn.onclick = () => {
|
||
selectedWorldId = f.world_id;
|
||
overlay.remove();
|
||
styleEl.remove();
|
||
resolve(selectedWorldId);
|
||
};
|
||
fDiv.appendChild(joinBtn);
|
||
}
|
||
body.appendChild(fDiv);
|
||
}
|
||
} else if (data.friends && !data.friends.length) {
|
||
const noFriends = document.createElement('div');
|
||
noFriends.className = 'mc-label';
|
||
noFriends.style.color = '#888';
|
||
noFriends.textContent = 'Пока нет друзей';
|
||
body.appendChild(noFriends);
|
||
}
|
||
|
||
// Pending friend requests
|
||
if (data.pending && data.pending.length) {
|
||
const pLabel = document.createElement('div');
|
||
pLabel.className = 'mc-label';
|
||
pLabel.style.color = '#B86E00';
|
||
pLabel.textContent = '📨 Запросы в друзья:';
|
||
body.appendChild(pLabel);
|
||
for (const p of data.pending) {
|
||
const pItem = document.createElement('div');
|
||
pItem.className = 'mc-friend-row';
|
||
const span = document.createElement('span');
|
||
span.textContent = p.from;
|
||
pItem.appendChild(span);
|
||
const btns = document.createElement('div');
|
||
btns.style.display = 'flex';
|
||
btns.style.gap = '4px';
|
||
const acceptBtn = document.createElement('button');
|
||
acceptBtn.className = 'mc-friend-btn mc-friend-btn-green';
|
||
acceptBtn.textContent = '✓';
|
||
acceptBtn.onclick = async () => {
|
||
const r = await fetch(state.SERVER_URL + '/api/friends/accept', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ tg_id: tgUser.id, request_id: p.id })
|
||
});
|
||
const d = await r.json();
|
||
if (d.ok) { showToast('✅ Друг добавлен!'); btnFriends.onclick(); } else { showToast('❌ Ошибка'); }
|
||
};
|
||
const declineBtn = document.createElement('button');
|
||
declineBtn.className = 'mc-friend-btn mc-friend-btn-red';
|
||
declineBtn.textContent = '✕';
|
||
declineBtn.onclick = async () => {
|
||
await fetch(state.SERVER_URL + '/api/friends/decline', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ tg_id: tgUser.id, request_id: p.id })
|
||
});
|
||
showToast('Отклонено');
|
||
btnFriends.onclick();
|
||
};
|
||
btns.appendChild(acceptBtn);
|
||
btns.appendChild(declineBtn);
|
||
pItem.appendChild(btns);
|
||
body.appendChild(pItem);
|
||
}
|
||
}
|
||
|
||
// Add friend
|
||
body.appendChild(document.createElement('hr'));
|
||
const addLabel = document.createElement('div');
|
||
addLabel.className = 'mc-label';
|
||
addLabel.textContent = '➕ Добавить друга по нику:';
|
||
body.appendChild(addLabel);
|
||
const addRow = document.createElement('div');
|
||
addRow.style.display = 'flex';
|
||
addRow.style.gap = '4px';
|
||
const addInput = document.createElement('input');
|
||
addInput.className = 'mc-input';
|
||
addInput.style.flex = '1';
|
||
addInput.type = 'text';
|
||
addInput.maxLength = 16;
|
||
addInput.placeholder = 'Ник друга';
|
||
const addBtn = document.createElement('button');
|
||
addBtn.className = 'mc-btn mc-btn-blue';
|
||
addBtn.style.width = 'auto';
|
||
addBtn.style.marginBottom = '0';
|
||
addBtn.style.padding = '8px 16px';
|
||
addBtn.textContent = '+';
|
||
addBtn.onclick = async () => {
|
||
const nick = addInput.value.trim().toLowerCase();
|
||
if (!nick || nick.length < 2) { showToast('❌ Минимум 2 символа'); return; }
|
||
const r = await fetch(state.SERVER_URL + '/api/friends/request', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ tg_id: tgUser.id, nickname: nick })
|
||
});
|
||
const d = await r.json();
|
||
if (d.ok) { showToast(d.message === 'Already friends' ? '✅ Уже друзья!' : '📨 Запрос отправлен!'); }
|
||
else { showToast('❌ ' + (d.error || 'Ошибка')); }
|
||
};
|
||
addRow.appendChild(addInput);
|
||
addRow.appendChild(addBtn);
|
||
body.appendChild(addRow);
|
||
|
||
// Privacy checkboxes
|
||
const settings = data.settings || { allow_requests: 1, notify_online: 1 };
|
||
const settingsDiv = document.createElement('div');
|
||
settingsDiv.style.marginTop = '12px';
|
||
const reqLabel = document.createElement('label');
|
||
reqLabel.className = 'mc-checkbox-row';
|
||
const reqCb = document.createElement('input');
|
||
reqCb.className = 'mc-checkbox';
|
||
reqCb.type = 'checkbox';
|
||
reqCb.checked = settings.allow_requests;
|
||
reqCb.onchange = async () => {
|
||
await fetch(state.SERVER_URL + '/api/friends/settings', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
tg_id: tgUser.id,
|
||
allow_requests: reqCb.checked ? 1 : 0,
|
||
notify_online: settings.notify_online
|
||
})
|
||
});
|
||
};
|
||
reqLabel.appendChild(reqCb);
|
||
reqLabel.appendChild(document.createTextNode(' Запросы в друзья'));
|
||
settingsDiv.appendChild(reqLabel);
|
||
body.appendChild(settingsDiv);
|
||
} catch (e) { showToast('❌ Ошибка загрузки друзей'); console.error(e); }
|
||
};
|
||
body.appendChild(btnFriends);
|
||
}
|
||
|
||
// Player name input (only if not already set)
|
||
if (!state.playerName) {
|
||
const nameDivider = document.createElement('hr');
|
||
nameDivider.className = 'mc-divider';
|
||
body.appendChild(nameDivider);
|
||
const nameLabel = document.createElement('div');
|
||
nameLabel.className = 'mc-label';
|
||
nameLabel.textContent = 'Твоё имя:';
|
||
body.appendChild(nameLabel);
|
||
const nameInput = document.createElement('input');
|
||
nameInput.className = 'mc-input';
|
||
nameInput.type = 'text';
|
||
nameInput.placeholder = 'Ваше имя';
|
||
nameInput.value = tgUser ? (tgUser.username || tgUser.first_name || '') : '';
|
||
nameInput.maxLength = 20;
|
||
nameInput.addEventListener('input', () => {
|
||
if (nameInput.value.trim()) {
|
||
state.playerName = nameInput.value.trim();
|
||
localStorage.setItem('minegrechka_playerName', state.playerName);
|
||
}
|
||
});
|
||
body.appendChild(nameInput);
|
||
}
|
||
|
||
box.appendChild(body);
|
||
overlay.appendChild(box);
|
||
document.body.appendChild(overlay);
|
||
|
||
// Pre-fill player name from TG if available
|
||
if (isTgWebApp && tgUser && !state.playerName) {
|
||
state.playerName = tgUser.username || tgUser.first_name || 'Игрок';
|
||
localStorage.setItem('minegrechka_playerName', state.playerName);
|
||
}
|
||
|
||
// Auto-load TG save info (background fetch)
|
||
if (isTgWebApp && tgUser && !worldParam) {
|
||
fetch(state.SERVER_URL + '/api/tg/save?tg_id=' + tgUser.id)
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.ok && data.world_id) {
|
||
// could update UI but menu is already shown
|
||
}
|
||
})
|
||
.catch(() => {});
|
||
}
|
||
});
|
||
}
|