Chat / index.html
noah33565's picture
Update index.html
d6edb9a verified
<!DOCTYPE html>
<html lang="de">
<head>
<meta name="description" content="NoahsChat – Ein Chatroom von NoahsAPP">
<meta name="keywords" content="NoahsChat, Chat, NoahsAPP, Noahs Chat, Noah, Discord, Roblox, Bann, Wichtig, Help, Support, Hacks, Jura">
<!-- Für schöne Vorschau beim Teilen -->
<meta property="og:title" content="NoahsChat">
<meta property="og:description" content="Ein Chatroom von NoahsAPP (https://noah33565-noahsapp.static.hf.space)">
<meta property="og:url" content="https://noah33565-chat.static.hf.space">
<meta name="google-site-verification" content="ndJbshO4CFOPURJ44uy8W8qBR_JMsq-gWqqqLtFODtc" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NoahsChat / Noahs Chat</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- BANNED SCREEN -->
<div id="banned-screen">
<div class="banned-icon">🚫</div>
<div class="banned-title">GEBANNT</div>
<div class="banned-sub" id="banned-sub"></div>
</div>
<!-- AUTH SCREEN -->
<div id="auth-screen">
<div class="auth-box">
<div class="auth-logo">Noahs<span>Chat</span></div>
<div class="auth-tagline">by NoahsAPP · v2.0</div>
<div class="auth-tabs">
<button class="auth-tab active" onclick="switchTab('login')">Login</button>
<button class="auth-tab" onclick="switchTab('register')">Registrieren</button>
</div>
<!-- LOGIN -->
<div id="tab-login">
<div class="auth-field">
<label>Username</label>
<input type="text" id="login-user" placeholder="dein username" autocomplete="off" onkeydown="if(event.key==='Enter')doLogin()">
</div>
<div class="auth-field">
<label>Passwort</label>
<input type="password" id="login-pass" placeholder="••••••••" onkeydown="if(event.key==='Enter')doLogin()">
</div>
<div class="captcha-box">
<div>
<div style="font-size:.6rem;color:var(--muted);letter-spacing:.15em;text-transform:uppercase;margin-bottom:4px">Anti-Bot</div>
<div class="captcha-question" id="login-q">? + ? = ?</div>
</div>
<input class="captcha-input" type="number" id="login-a" placeholder="?" onkeydown="if(event.key==='Enter')doLogin()">
<button class="captcha-refresh" onclick="refreshCaptcha('login')" title="Neu"></button>
</div>
<button class="auth-btn" onclick="doLogin()">Einloggen ▶</button>
</div>
<!-- REGISTER -->
<div id="tab-register" style="display:none">
<div class="auth-field">
<label>Username</label>
<input type="text" id="reg-user" placeholder="wähle einen username" autocomplete="off">
</div>
<div class="auth-field">
<label>Passwort</label>
<input type="password" id="reg-pass" placeholder="mindestens 4 Zeichen">
</div>
<div class="auth-field">
<label>Passwort bestätigen</label>
<input type="password" id="reg-pass2" placeholder="nochmal eingeben" onkeydown="if(event.key==='Enter')doRegister()">
</div>
<div class="captcha-box">
<div>
<div style="font-size:.6rem;color:var(--muted);letter-spacing:.15em;text-transform:uppercase;margin-bottom:4px">Anti-Bot</div>
<div class="captcha-question" id="reg-q">? + ? = ?</div>
</div>
<input class="captcha-input" type="number" id="reg-a" placeholder="?" onkeydown="if(event.key==='Enter')doRegister()">
<button class="captcha-refresh" onclick="refreshCaptcha('reg')" title="Neu"></button>
</div>
<button class="auth-btn" onclick="doRegister()">Account erstellen ▶</button>
</div>
<div class="auth-error" id="auth-error"></div>
</div>
</div>
<!-- MAIN APP -->
<div id="app">
<!-- TOPBAR -->
<div class="topbar">
<a href="#" class="topbar-logo">Noahs<span>APP</span></a>
<div class="topbar-sep"></div>
<div class="topbar-channel">
<span id="topbar-channel-name"># allgemein</span>
</div>
<div class="topbar-actions">
<button id="mobile-menu-btn" onclick="toggleMobileSidebar()" title="Menü" style="display:none"></button>
<button class="icon-btn" onclick="openSettings()" title="Einstellungen">⚙️</button>
<div class="topbar-sep"></div>
<div class="topbar-user">
<div class="user-avatar-sm" id="topbar-avatar" onclick="openSettings()"></div>
<span id="topbar-username" style="font-weight:700"></span>
<span class="topbar-role-badge" id="topbar-role-badge"></span>
<button class="logout-btn" onclick="doLogout()">Logout</button>
</div>
</div>
</div>
<!-- MOBILE SIDEBAR BACKDROP -->
<div id="sidebar-backdrop" onclick="closeMobileSidebar()" style="display:none"></div>
<!-- SIDEBAR -->
<div class="sidebar" id="sidebar">
<div class="sidebar-scroll">
<!-- Channels -->
<div class="sidebar-section">
Channels
<span id="online-count" style="color:var(--green);font-family:'Orbitron',monospace">0</span>
</div>
<div id="channel-list"></div>
<div class="sidebar-divider"></div>
<!-- DMs -->
<div class="sidebar-section">Direktnachrichten</div>
<div id="dm-list"></div>
<div class="sidebar-divider"></div>
<!-- Members -->
<div class="sidebar-section">Mitglieder</div>
<div id="user-list"></div>
</div>
<!-- Voice Panel -->
<div class="voice-panel" id="voice-panel">
<div class="voice-status-bar">
<div class="voice-active-dot"></div>
<div class="voice-name" id="voice-channel-name">🔊 Voice</div>
<div class="voice-controls">
<button class="voice-ctrl" id="voice-mute-btn" onclick="toggleMute()">🎙 Stumm</button>
<button class="voice-ctrl" id="voice-deaf-btn" onclick="toggleDeafen()">🔔 Taub</button>
<button class="voice-ctrl" style="color:var(--danger)" onclick="leaveVoice()"></button>
</div>
</div>
</div>
</div>
<!-- CHAT AREA -->
<div class="chat-area">
<div class="chat-header">
<div class="chat-header-info">
<div class="chat-header-title" id="chat-title">💬 allgemein</div>
<div class="chat-header-sub" id="chat-sub">Allgemeiner Chat für alle</div>
</div>
<div class="chat-header-actions">
<button class="icon-btn" id="search-btn" title="Suchen" onclick="openSearchModal()">🔍</button>
<button class="icon-btn" id="pins-btn" title="Angepinnte Nachrichten" onclick="openPinsModal()">📌</button>
</div>
</div>
<div class="messages" id="messages">
<div class="empty-chat">
<div class="empty-chat-icon">💬</div>
<p>Noch keine Nachrichten. Schreib was!</p>
</div>
</div>
<div id="timeout-bar" class="timeout-bar" style="display:none">
⏱ Du bist stummgeschaltet bis <span id="timeout-until"></span>
</div>
<div class="input-area">
<textarea class="msg-input" id="msg-input" placeholder="Nachricht schreiben..."
rows="1" onkeydown="handleKey(event)" oninput="autoResize(this)"></textarea>
<button class="send-btn" id="send-btn" onclick="sendMessage()">SEND</button>
</div>
</div>
</div>
<!-- CONTEXT MENU -->
<div id="context-menu" style="display:none"></div>
<!-- MODAL CONTAINER -->
<div id="modal-container"></div>
<script>
// ─── DEFAULT DATA (from data.json equivalent) ─────────────────
window.DEFAULT_CHANNELS = [
{ id: 'ankuendigungen', name: 'ankündigungen', icon: '📢', sub: 'Wichtige Ankündigungen', adminOnly: true, category: 'info' },
{ id: 'regeln', name: 'regeln', icon: '📋', sub: 'Serverregeln', readOnly: true, category: 'info' },
{ id: 'allgemein', name: 'allgemein', icon: '💬', sub: 'Allgemeiner Chat für alle', category: 'chat' },
{ id: 'gaming', name: 'gaming', icon: '🎮', sub: 'Alles rund ums Gaming', category: 'chat' },
{ id: 'roblox', name: 'roblox', icon: '🧱', sub: 'Roblox & NoahsHacks', category: 'chat' },
{ id: 'musik', name: 'musik', icon: '🎵', sub: 'Musik & Playlists', category: 'chat' },
{ id: 'memes', name: 'memes', icon: '😂', sub: 'Memes & Witze', category: 'fun' },
{ id: 'off-topic', name: 'off-topic', icon: '💭', sub: 'Alles andere', category: 'fun' },
{ id: 'kunst', name: 'kunst', icon: '🎨', sub: 'Kreative Werke', category: 'fun' },
{ id: 'technik', name: 'technik', icon: '💻', sub: 'Tech & Coding', category: 'fun' },
{ id: 'voicetext', name: 'voice-lounge', icon: '🔊', sub: 'Begleitchat für Voice', category: 'chat' },
{ id: 'moderators', name: 'mod-logs', icon: '🛡', sub: 'Moderationslog', modOnly: true, category: 'staff' },
{ id: 'admin', name: 'admin-bereich', icon: '⚙️', sub: 'Nur für Admins', adminOnly: true, category: 'staff' }
];
window.DEFAULT_AUTOMOD = {
enabled: true,
maxMsgLength: 800,
spamThreshold: 5,
spamWindow: 10000,
bannedWords: ['scheiß', 'hurensohn', 'wichser', 'fick', 'arschloch'],
linkFilter: false,
capsFilter: true,
capsThreshold: 0.7
};
</script>
<!-- Search & Pins modals -->
<script>
async function openSearchModal() {
showModal(`
<div class="modal-title">🔍 Nachrichten durchsuchen</div>
<div class="modal-field">
<label>Suchbegriff</label>
<input type="text" id="search-q" placeholder="Suche im aktuellen Channel..." oninput="liveSearch(this.value)">
</div>
<div id="search-results" style="max-height:300px;overflow-y:auto;margin-top:10px"></div>
<div class="modal-btns">
<button class="modal-btn secondary" onclick="clearModal()">Schließen</button>
</div>`);
}
async function liveSearch(query) {
const results = document.getElementById('search-results');
if (!query || query.length < 2) { results.innerHTML = ''; return; }
const msgs = (await DB.get('msgs_' + currentChannel)) || [];
const found = msgs.filter(m => !m.system && m.text && m.text.toLowerCase().includes(query.toLowerCase()));
if (!found.length) { results.innerHTML = '<div style="color:var(--muted);text-align:center;padding:12px;font-size:.85rem">Keine Ergebnisse</div>'; return; }
results.innerHTML = found.slice(-20).map(m => `
<div style="padding:8px 0;border-bottom:1px solid var(--border)">
<div style="font-size:.72rem;color:var(--muted);font-family:Orbitron,monospace">${m.author} · ${new Date(m.ts).toLocaleDateString('de-DE')}</div>
<div style="font-size:.9rem;color:var(--text)">${escHtml(m.text).replace(new RegExp(escHtml(query),'gi'), s => `<mark style="background:rgba(0,212,255,0.2);color:var(--accent)">${s}</mark>`)}</div>
</div>`).join('');
}
async function openPinsModal() {
const pins = (await DB.get('pins')) || [];
const relevant = pins.filter(p => p.channel === currentChannel);
showModal(`
<div class="modal-title">📌 Angepinnte Nachrichten</div>
<div style="max-height:350px;overflow-y:auto">
${!relevant.length ? '<div style="color:var(--muted);text-align:center;padding:20px;font-size:.85rem">Keine angepinnten Nachrichten</div>' :
relevant.map(p => `
<div style="padding:10px 0;border-bottom:1px solid var(--border)">
<div style="font-size:.7rem;color:var(--muted);font-family:Orbitron,monospace">📌 ${p.author} · angeheftet von ${p.by}</div>
<div style="font-size:.9rem;color:var(--text);margin-top:4px">${escHtml(p.text)}</div>
</div>`).join('')}
</div>
<div class="modal-btns">
<button class="modal-btn secondary" onclick="clearModal()">Schließen</button>
</div>`);
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/1.5.4/peerjs.min.js"></script>
<script src="app.js"></script>
<script>
// ─── MOBILE SIDEBAR TOGGLE ─────────────────────────────────────
function isMobile() { return window.innerWidth <= 768; }
function toggleMobileSidebar() {
const sidebar = document.getElementById('sidebar');
const backdrop = document.getElementById('sidebar-backdrop');
const open = sidebar.classList.toggle('open');
if (open) {
backdrop.style.display = 'block';
backdrop.classList.add('visible');
} else {
backdrop.classList.remove('visible');
backdrop.style.display = 'none';
}
}
function closeMobileSidebar() {
document.getElementById('sidebar').classList.remove('open');
const backdrop = document.getElementById('sidebar-backdrop');
backdrop.classList.remove('visible');
backdrop.style.display = 'none';
}
// Show/hide hamburger based on screen size
function updateMobileUI() {
const btn = document.getElementById('mobile-menu-btn');
if (btn) btn.style.display = isMobile() ? 'flex' : 'none';
}
window.addEventListener('resize', updateMobileUI);
document.addEventListener('DOMContentLoaded', updateMobileUI);
updateMobileUI();
// Close sidebar when a channel is selected on mobile
document.addEventListener('click', function(e) {
if (!isMobile()) return;
const item = e.target.closest('.channel-item, .user-item');
if (item) closeMobileSidebar();
});
// Fix iOS viewport resize when keyboard opens
function fixViewportOnKeyboard() {
if (!isMobile()) return;
const chatArea = document.querySelector('.chat-area');
if (!chatArea) return;
// Scroll to bottom when keyboard opens
const input = document.getElementById('msg-input');
if (input) {
input.addEventListener('focus', () => {
setTimeout(() => {
const msgs = document.getElementById('messages');
if (msgs) msgs.scrollTop = msgs.scrollHeight;
}, 350);
});
}
}
document.addEventListener('DOMContentLoaded', fixViewportOnKeyboard);
</script>
</body>
</html>