|
|
<!DOCTYPE html> |
|
|
<html lang="es"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<title>DarkChat</title> |
|
|
<style> |
|
|
body { |
|
|
font-family: Arial, sans-serif; |
|
|
background: #121212; |
|
|
color: #eee; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
height: 100vh; |
|
|
margin: 0; |
|
|
} |
|
|
#login-container, #chat-container { |
|
|
background: rgba(30,30,30,0.95); |
|
|
padding: 20px; |
|
|
border-radius: 12px; |
|
|
box-shadow: 0 0 12px rgba(0,0,0,0.5); |
|
|
width: 350px; |
|
|
max-height: 95vh; |
|
|
overflow: auto; |
|
|
position: relative; |
|
|
} |
|
|
#chat-container { |
|
|
|
|
|
background-image: url("https://www.ecartelera.com/images/noticias/fotos/56800/56875/1.jpg"); |
|
|
background-size: cover; |
|
|
background-position: center; |
|
|
} |
|
|
h2 { margin-top: 0; text-align: center; } |
|
|
input, button { |
|
|
width: 100%; margin: 5px 0; padding: 10px; |
|
|
border: none; border-radius: 6px; font-size: 14px; |
|
|
} |
|
|
input { background: #2b2b2b; color: #eee; } |
|
|
button { background: #3a3a3a; color: #eee; cursor: pointer; } |
|
|
button:hover { background: #575757; } |
|
|
#messages { |
|
|
height: 300px; overflow-y: auto; |
|
|
border: 1px solid #333; padding: 10px; |
|
|
background: rgba(15,15,15,0.9); |
|
|
margin-bottom: 10px; border-radius: 6px; |
|
|
} |
|
|
.message { display: flex; align-items: center; margin-bottom: 8px; } |
|
|
.message img { |
|
|
width: 28px; height: 28px; border-radius: 50%; margin-right: 8px; cursor: pointer; |
|
|
} |
|
|
.username { font-weight: bold; margin-right: 6px; color: #4da6ff; } |
|
|
.time { font-size: 11px; color: #aaa; margin-left: auto; } |
|
|
.date-separator { |
|
|
text-align: center; |
|
|
margin: 10px 0; |
|
|
font-size: 12px; |
|
|
color: #aaa; |
|
|
border-bottom: 1px solid #333; |
|
|
line-height: 0.1em; |
|
|
} |
|
|
.date-separator span { background: rgba(30,30,30,0.95); padding: 0 5px; } |
|
|
#logout { |
|
|
background: #d9534f; font-size: 12px; padding: 6px 10px; width: auto; float: right; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
|
|
|
<div id="login-container"> |
|
|
<h2>DarkChat</h2> |
|
|
<input type="email" id="email" placeholder="Email"> |
|
|
<input type="password" id="password" placeholder="Contraseña"> |
|
|
<input type="text" id="username" placeholder="Nombre de usuario (solo registro)"> |
|
|
<input type="file" id="avatar" accept="image/*"> |
|
|
<button id="register">Registrarse</button> |
|
|
<button id="login">Iniciar sesión</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="chat-container" style="display:none;"> |
|
|
<h2>DarkChat<button id="logout">Salir</button></h2> |
|
|
<div id="messages"></div> |
|
|
<input type="text" id="messageInput" placeholder="Escribe un mensaje..."> |
|
|
<button id="send">Enviar</button> |
|
|
</div> |
|
|
|
|
|
<script type="module"> |
|
|
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.3/firebase-app.js"; |
|
|
import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, onAuthStateChanged, updateProfile } from "https://www.gstatic.com/firebasejs/10.12.3/firebase-auth.js"; |
|
|
import { getDatabase, ref, push, onChildAdded, set, update } from "https://www.gstatic.com/firebasejs/10.12.3/firebase-database.js"; |
|
|
|
|
|
const firebaseConfig = { |
|
|
apiKey: "AIzaSyBafUK6E2w7oNIMXEGgajzRO62_APcZwYc", |
|
|
authDomain: "darkchat-afa0f.firebaseapp.com", |
|
|
databaseURL: "https://darkchat-afa0f-default-rtdb.firebaseio.com", |
|
|
projectId: "darkchat-afa0f", |
|
|
storageBucket: "darkchat-afa0f.firebasestorage.app", |
|
|
messagingSenderId: "664872777437", |
|
|
appId: "1:664872777437:web:6ca28b81ab217ae9c10d34" |
|
|
}; |
|
|
|
|
|
const app = initializeApp(firebaseConfig); |
|
|
const auth = getAuth(app); |
|
|
const db = getDatabase(app); |
|
|
|
|
|
const loginContainer = document.getElementById("login-container"); |
|
|
const chatContainer = document.getElementById("chat-container"); |
|
|
const messagesDiv = document.getElementById("messages"); |
|
|
const messageInput = document.getElementById("messageInput"); |
|
|
|
|
|
const registerBtn = document.getElementById("register"); |
|
|
const loginBtn = document.getElementById("login"); |
|
|
const sendBtn = document.getElementById("send"); |
|
|
const logoutBtn = document.getElementById("logout"); |
|
|
|
|
|
const imgbbApiKey = "b0b6596b38afbd70e418415eca9717cb"; |
|
|
|
|
|
async function uploadImage(file) { |
|
|
const formData = new FormData(); |
|
|
formData.append("image", file); |
|
|
const res = await fetch(`https://api.imgbb.com/1/upload?key=${imgbbApiKey}`, { method: "POST", body: formData }); |
|
|
const data = await res.json(); |
|
|
return data.data.url; |
|
|
} |
|
|
|
|
|
registerBtn.addEventListener("click", async () => { |
|
|
const email = document.getElementById("email").value; |
|
|
const password = document.getElementById("password").value; |
|
|
const username = document.getElementById("username").value; |
|
|
const avatarFile = document.getElementById("avatar").files[0]; |
|
|
if (!username || !avatarFile) { alert("Debes ingresar un nombre y un avatar."); return; } |
|
|
try { |
|
|
const userCred = await createUserWithEmailAndPassword(auth, email, password); |
|
|
const user = userCred.user; |
|
|
const avatarUrl = await uploadImage(avatarFile); |
|
|
await updateProfile(user, { displayName: username, photoURL: avatarUrl }); |
|
|
await set(ref(db, "users/" + user.uid), { username, avatar: avatarUrl }); |
|
|
alert("Registro exitoso 🎉"); |
|
|
} catch (e) { alert(e.message); } |
|
|
}); |
|
|
|
|
|
loginBtn.addEventListener("click", async () => { |
|
|
try { await signInWithEmailAndPassword(auth, email.value, password.value); } |
|
|
catch (e) { alert(e.message); } |
|
|
}); |
|
|
|
|
|
logoutBtn.addEventListener("click", async () => { await signOut(auth); }); |
|
|
|
|
|
function formatWhatsAppTime(ts) { |
|
|
const date = new Date(ts); |
|
|
const now = new Date(); |
|
|
const isToday = date.toDateString() === now.toDateString(); |
|
|
const yesterday = new Date(); yesterday.setDate(now.getDate() - 1); |
|
|
const isYesterday = date.toDateString() === yesterday.toDateString(); |
|
|
const time = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); |
|
|
if (isToday) return time; |
|
|
if (isYesterday) return `Ayer ${time}`; |
|
|
return `${date.getDate()}/${date.getMonth()+1} ${time}`; |
|
|
} |
|
|
|
|
|
async function sendMessage() { |
|
|
const text = messageInput.value.trim(); |
|
|
if (!text) return; |
|
|
const user = auth.currentUser; |
|
|
if (!user) return; |
|
|
await push(ref(db, "messages"), { |
|
|
text, |
|
|
uid: user.uid, |
|
|
username: user.displayName || "Anónimo", |
|
|
avatar: user.photoURL || "", |
|
|
timestamp: Date.now() |
|
|
}); |
|
|
messageInput.value = ""; |
|
|
} |
|
|
sendBtn.addEventListener("click", sendMessage); |
|
|
messageInput.addEventListener("keydown", e => { if(e.key==="Enter") sendMessage(); }); |
|
|
|
|
|
let lastMessageDate = null; |
|
|
|
|
|
onChildAdded(ref(db,"messages"), snapshot => { |
|
|
const msg = snapshot.val(); |
|
|
const messageDate = new Date(msg.timestamp).toDateString(); |
|
|
|
|
|
if (messageDate !== lastMessageDate) { |
|
|
lastMessageDate = messageDate; |
|
|
const sepDiv = document.createElement("div"); |
|
|
sepDiv.className = "date-separator"; |
|
|
const now = new Date(); |
|
|
const yesterday = new Date(); yesterday.setDate(now.getDate()-1); |
|
|
let text = ""; |
|
|
if (messageDate === now.toDateString()) text="Hoy"; |
|
|
else if (messageDate === yesterday.toDateString()) text="Ayer"; |
|
|
else { const d = new Date(msg.timestamp); text=`${d.getDate()}/${d.getMonth()+1}/${d.getFullYear()}`; } |
|
|
sepDiv.innerHTML = `<span>${text}</span>`; |
|
|
messagesDiv.appendChild(sepDiv); |
|
|
} |
|
|
|
|
|
const div = document.createElement("div"); div.className="message"; |
|
|
const img = document.createElement("img"); img.src=msg.avatar||"https://via.placeholder.com/28"; img.title="Click para cambiar avatar"; |
|
|
const nameSpan=document.createElement("span"); nameSpan.className="username"; nameSpan.textContent=msg.username+":"; |
|
|
const textSpan=document.createElement("span"); textSpan.textContent=" "+msg.text; |
|
|
const timeSpan=document.createElement("span"); timeSpan.className="time"; timeSpan.textContent=formatWhatsAppTime(msg.timestamp); |
|
|
|
|
|
div.appendChild(img); div.appendChild(nameSpan); div.appendChild(textSpan); div.appendChild(timeSpan); |
|
|
|
|
|
img.addEventListener("click", async () => { |
|
|
if(auth.currentUser && msg.uid===auth.currentUser.uid){ |
|
|
const fileInput=document.createElement("input"); fileInput.type="file"; fileInput.accept="image/*"; fileInput.click(); |
|
|
fileInput.onchange=async()=>{ |
|
|
const newFile=fileInput.files[0]; |
|
|
if(newFile){ |
|
|
const newUrl=await uploadImage(newFile); |
|
|
await updateProfile(auth.currentUser,{photoURL:newUrl}); |
|
|
await update(ref(db,"users/"+auth.currentUser.uid),{avatar:newUrl}); |
|
|
img.src=newUrl; alert("Avatar actualizado ✅"); |
|
|
} |
|
|
}; |
|
|
} |
|
|
}); |
|
|
|
|
|
messagesDiv.appendChild(div); |
|
|
messagesDiv.scrollTop=messagesDiv.scrollHeight; |
|
|
}); |
|
|
|
|
|
onAuthStateChanged(auth, user => { |
|
|
loginContainer.style.display = user ? "none" : "block"; |
|
|
chatContainer.style.display = user ? "block" : "none"; |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|