// Main app controller
const App = {
consentGiven: false,
async init() {
// bind tabbar
document.querySelectorAll('.tab').forEach(btn => {
btn.onclick = () => this.showView(btn.dataset.view);
});
// restore tokens from localStorage
API.Store.load();
if (TG_APP.isAvailable && TG_APP.initData) {
// авторизация через Telegram
try {
await API.Auth.loginWithTelegram(TG_APP.initData);
await this.checkConsent();
this.showView('map');
} catch (e) {
this.showError('Не удалось войти через Telegram: ' + e.message);
}
} else if (API.Store.access) {
// уже залогинен
try {
await API.Auth.me();
await this.checkConsent();
this.showView('map');
} catch (e) {
API.Store.clear();
this.showError('Сессия истекла: ' + e.message);
}
} else {
// WebApp открыт вне Telegram — предложим email login
this.showEmailLogin();
}
// bind input events for chat
document.body.addEventListener('click', (e) => {
const sendBtn = e.target.closest('#msg-send');
if (sendBtn) { window.ChatView.send(); return; }
const cancel = e.target.closest('#edit-cancel');
if (cancel) { document.getElementById('msg-input').value = ''; document.getElementById('edit-bar').style.display='none'; return; }
});
document.body.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && e.target.id === 'msg-input') {
if (!e.shiftKey) { e.preventDefault(); window.ChatView.send(); }
}
});
},
async checkConsent() {
// показываем согласия при первом входе
if (API.Store.user && !API.Store.user.consents_given) {
this.showConsent();
return false;
}
return true;
},
showConsent() {
const ov = document.createElement('div');
ov.id = 'consent-overlay';
ov.innerHTML = `
🍻 Добро пожаловать в BuhApp
Приложение для поиска компании. Только для лиц 18+.
`;
document.body.appendChild(ov);
document.getElementById('c-accept').onclick = async () => {
const a = document.getElementById('c-adult').checked;
const t = document.getElementById('c-terms').checked;
const p = document.getElementById('c-privacy').checked;
const d = document.getElementById('c-disclaimer').checked;
if (!a || !t || !p || !d) {
TG_APP.showAlert('Нужно принять все 4 пункта');
return;
}
try {
await API.api('/api/v1/auth/consents', {
method: 'POST',
body: {
adult: a, terms: t, privacy: p, disclaimer: d,
},
});
TG_APP.haptic('notification');
ov.remove();
this.consentGiven = true;
this.showView('map');
} catch (e) {
TG_APP.showAlert('Ошибка: ' + e.message);
}
};
},
showError(msg) {
document.getElementById('app').innerHTML = `
`;
},
showEmailLogin() {
document.getElementById('app').innerHTML = `
🍻 BuhApp
Приложение для поиска компании (Telegram Mini App)
🍻 BuhApp — приложение для лиц 18+. Встречи и употребление алкоголя — на свой риск.
Принимая соглашение, вы подтверждаете совершеннолетие.
`;
document.getElementById('el-login').onclick = () => this.emailLogin();
document.getElementById('el-register').onclick = () => this.emailRegister();
},
async emailLogin() {
const email = document.getElementById('el-email').value.trim();
const pass = document.getElementById('el-pass').value;
try {
await API.Auth.loginEmail(email, pass);
await API.Auth.me();
await this.checkConsent();
this.showView('map');
} catch (e) {
TG_APP.showAlert('Ошибка: ' + e.message);
}
},
async emailRegister() {
const email = document.getElementById('el-email').value.trim();
const pass = document.getElementById('el-pass').value;
if (!email || pass.length < 8) {
TG_APP.showAlert('Email и пароль (≥8 символов) обязательны');
return;
}
try {
await API.Auth.registerEmail({
email,
password: pass,
name: email.split('@')[0],
consents: { adult: true, terms: true, privacy: true, disclaimer: true },
});
await API.Auth.me();
this.consentGiven = true;
this.showView('map');
} catch (e) {
TG_APP.showAlert('Ошибка: ' + e.message);
}
},
showView(name, params = {}) {
document.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', t.dataset.view === name));
const root = document.getElementById('app');
if (window.ChatView?.cleanup) window.ChatView.cleanup();
switch (name) {
case 'map': MapView.render(root); break;
case 'chats': ChatsView.render(root); break;
case 'chat': ChatView.render(root, params); break;
case 'profile': ProfileView.render(root); break;
case 'settings': SettingsView.render(root); break;
case 'review': ReviewView.render(root, params); break;
case 'user-reviews': UserReviewsView.render(root, params); break;
case 'user-profile': UserProfileView.render(root, params); break;
default: root.innerHTML = 'Неизвестный экран
';
}
},
openChat(chatId, otherId, otherName) {
this.showView('chat', { chatId, otherId, otherName });
},
openReview(chatId, otherId, otherName) {
this.showView('review', { chatId, otherId, otherName });
},
openUserProfile(userId, name) {
this.showView('user-profile', { userId, name });
},
toast(msg) {
const t = document.createElement('div');
t.className = 'toast';
t.textContent = msg;
document.body.appendChild(t);
setTimeout(() => t.remove(), 2000);
},
};
// helpers
function escapeHtml(s) {
if (!s) return '';
return String(s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
}
function formatTime(iso) {
if (!iso) return '';
const d = new Date(iso);
const today = new Date();
if (d.toDateString() === today.toDateString()) {
return d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0');
}
return d.getDate().toString().padStart(2, '0') + '.' + (d.getMonth() + 1).toString().padStart(2, '0');
}
function formatDate(iso) {
if (!iso) return '';
const d = new Date(iso);
return d.getDate().toString().padStart(2, '0') + '.' + (d.getMonth() + 1).toString().padStart(2, '0') + '.' + d.getFullYear();
}
window.App = App;
document.addEventListener('DOMContentLoaded', () => App.init());