All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 30s
Streamer dashboard gains an editable AI assistant name (default "Альтушка Ирина"), stored per-streamer and used for the overlay label, TTS intro, and DeepSeek system prompt. Also fixes a bug where a long message's TTS audio could still be playing when the next queued message started its own audio — the overlay queue now waits for the current audio to actually finish (not just the configured display duration) before advancing.
110 lines
3.4 KiB
JavaScript
110 lines
3.4 KiB
JavaScript
const loginContainer = document.getElementById('login-container');
|
||
const mainSection = document.getElementById('hidden-until-login');
|
||
const userPic = document.getElementById('user-pic');
|
||
const userName = document.getElementById('user-name');
|
||
const logoutBtn = document.getElementById('logout');
|
||
const displayNameInput = document.getElementById('display-name');
|
||
const messageInput = document.getElementById('message');
|
||
const aiReplyCheckbox = document.getElementById('ai-reply');
|
||
const aiReplyLabel = document.getElementById('ai-reply-label');
|
||
const sendBtn = document.getElementById('send');
|
||
const statusEl = document.getElementById('status');
|
||
|
||
const token = location.pathname.split('/').filter(Boolean).pop();
|
||
const socket = io({ query: { role: 'sender', token } });
|
||
|
||
function showLoggedIn(user) {
|
||
loginContainer.style.display = 'none';
|
||
mainSection.style.display = 'block';
|
||
userPic.src = user.picture || '';
|
||
userName.textContent = user.name || user.email;
|
||
}
|
||
|
||
function showLoggedOut() {
|
||
loginContainer.style.display = 'block';
|
||
mainSection.style.display = 'none';
|
||
}
|
||
|
||
async function loadDisplayName() {
|
||
const res = await fetch(`/api/viewer-name?token=${encodeURIComponent(token)}`);
|
||
if (!res.ok) return;
|
||
const { name } = await res.json();
|
||
displayNameInput.value = name || '';
|
||
}
|
||
|
||
async function handleCredentialResponse(response) {
|
||
const res = await fetch('/auth/google', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ credential: response.credential }),
|
||
});
|
||
|
||
if (res.ok) {
|
||
const { user } = await res.json();
|
||
showLoggedIn(user);
|
||
loadDisplayName();
|
||
// The socket connected before login with no session cookie; reconnect so
|
||
// its handshake picks up the freshly-issued, now-authenticated cookie.
|
||
socket.disconnect();
|
||
socket.connect();
|
||
} else {
|
||
statusEl.textContent = 'Не удалось войти, попробуйте ещё раз';
|
||
}
|
||
}
|
||
|
||
async function loadAiName() {
|
||
const res = await fetch(`/api/streamer-info?token=${encodeURIComponent(token)}`);
|
||
if (!res.ok) return;
|
||
const { aiName } = await res.json();
|
||
if (aiName) aiReplyLabel.textContent = `Задать вопрос: ${aiName} 🤖`;
|
||
}
|
||
|
||
async function init() {
|
||
loadAiName();
|
||
|
||
const configRes = await fetch('/config');
|
||
const { googleClientId } = await configRes.json();
|
||
|
||
google.accounts.id.initialize({
|
||
client_id: googleClientId,
|
||
callback: handleCredentialResponse,
|
||
});
|
||
google.accounts.id.renderButton(loginContainer, { theme: 'outline', size: 'large' });
|
||
|
||
const meRes = await fetch('/me');
|
||
const { user } = await meRes.json();
|
||
if (user) {
|
||
showLoggedIn(user);
|
||
loadDisplayName();
|
||
} else {
|
||
showLoggedOut();
|
||
}
|
||
}
|
||
|
||
sendBtn.addEventListener('click', () => {
|
||
const text = messageInput.value.trim();
|
||
if (!text) return;
|
||
const name = displayNameInput.value.trim();
|
||
const aiReply = aiReplyCheckbox.checked;
|
||
socket.emit('send_message', { token, text, name, aiReply });
|
||
messageInput.value = '';
|
||
statusEl.textContent = 'Отправлено';
|
||
setTimeout(() => { statusEl.textContent = ''; }, 1500);
|
||
});
|
||
|
||
messageInput.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
||
sendBtn.click();
|
||
}
|
||
});
|
||
|
||
socket.on('send_error', (msg) => {
|
||
statusEl.textContent = 'Ошибка: ' + msg;
|
||
});
|
||
|
||
logoutBtn.addEventListener('click', async () => {
|
||
await fetch('/auth/logout', { method: 'POST' });
|
||
showLoggedOut();
|
||
});
|
||
|
||
init();
|