| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0"> |
| <title>Chat</title> |
| </head> |
| <body style="margin: 0; font-family: Arial, sans-serif; background-color: #121212; color: #ffffff; display: flex; flex-direction: column; height: 100vh;"> |
| <header style="background-color: #1e1e1e; padding: 10px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #333;"> |
| <a href="/" style="color: #ffffff; text-decoration: none;">⬅ Back</a> |
| <h1 style="margin: 0; font-size: 1.2rem;">Chat</h1> |
| </header> |
| <main id="chat-box" style="flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 10px; background-color: #1c1c1c;"></main> |
| <footer style="display: flex; flex-direction: column; padding: 10px; background-color: #1e1e1e; border-top: 1px solid #333;"> |
| <div id="reply-preview" style="display: none; margin-bottom: 10px; padding: 8px; background: #333; border-left: 4px solid #4caf50; border-radius: 5px; color: #bbb; font-size: 0.9rem;"></div> |
| <div style="display: flex; align-items: center; width: 100%;"> |
| <input type="text" id="message-input" placeholder="Type a message" style="flex: 1; padding: 10px; border: 1px solid #444; border-radius: 20px; background: #2c2c2c; color: #fff; margin-right: 10px;"> |
| <input type="file" id="image-input" style="display: none;"> |
| <button id="send-image" style="background: #4caf50; border: none; color: white; padding: 10px 15px; border-radius: 50%; cursor: pointer;">📷</button> |
| <button id="send-message" style="background: #4caf50; border: none; color: white; padding: 10px 15px; border-radius: 50%; cursor: pointer;">Send</button> |
| </div> |
| </footer> |
|
|
| |
| <div id="key-modal" style="display: flex; flex-direction: column; align-items: center; justify-content: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); z-index: 1000;"> |
| <div style="background: #1e1e1e; padding: 20px; border-radius: 8px; text-align: center;"> |
| <h2 style="color: #fff;">Enter Key</h2> |
| <input id="key-input" type="password" placeholder="Enter your key" style="padding: 10px; width: 100%; border: 1px solid #444; border-radius: 5px; margin-top: 10px; color: #fff; background: #2c2c2c;"> |
| <button id="verify-key" style="margin-top: 10px; padding: 10px 20px; background: #4caf50; border: none; color: #fff; cursor: pointer; border-radius: 5px;">Verify</button> |
| </div> |
| </div> |
|
|
| <script src="/socket.io/socket.io.js"></script> |
| <script> |
| const chatBox = document.getElementById('chat-box'); |
| const messageInput = document.getElementById('message-input'); |
| const sendMessageButton = document.getElementById('send-message'); |
| const sendImageButton = document.getElementById('send-image'); |
| const imageInput = document.getElementById('image-input'); |
| const replyPreview = document.getElementById('reply-preview'); |
| const keyModal = document.getElementById('key-modal'); |
| const keyInput = document.getElementById('key-input'); |
| const verifyKeyButton = document.getElementById('verify-key'); |
| const jid = window.location.pathname.split('/chat/')[1]; |
| let replyTo = null; |
| let userKey = localStorage.getItem('userKey'); |
| |
| |
| async function verifyKey(key) { |
| const res = await fetch('/validate-key', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ key }), |
| }); |
| return res.ok; |
| } |
| |
| |
| verifyKeyButton.addEventListener('click', async () => { |
| const enteredKey = keyInput.value.trim(); |
| if (!enteredKey) return alert('Please enter a key.'); |
| |
| const isValid = await verifyKey(enteredKey); |
| if (isValid) { |
| userKey = enteredKey; |
| localStorage.setItem('userKey', userKey); |
| keyModal.style.display = 'none'; |
| loadMessages(); |
| } else { |
| alert('Invalid key. Please try again.'); |
| } |
| }); |
| |
| |
| function addMessageToChat(msg) { |
| const messageDiv = document.createElement('div'); |
| messageDiv.style = ` |
| max-width: 70%; |
| padding: 10px; |
| border-radius: 8px; |
| background-color: ${msg.sender === 'You' ? '#4caf50' : '#2c2c2c'}; |
| color: #ffffff; |
| align-self: ${msg.sender === 'You' ? 'flex-end' : 'flex-start'}; |
| word-wrap: break-word; |
| cursor: pointer; |
| `; |
| |
| |
| if (msg.replyTo) { |
| const replyDiv = document.createElement('div'); |
| replyDiv.style = ` |
| margin-bottom: 8px; |
| padding: 5px; |
| border-left: 4px solid #888; |
| font-size: 0.85rem; |
| color: #bbb; |
| `; |
| replyDiv.textContent = msg.replyTo; |
| messageDiv.appendChild(replyDiv); |
| } |
| |
| |
| if (msg.imageUrl) { |
| const img = document.createElement('img'); |
| img.src = msg.imageUrl; |
| img.alt = 'Image'; |
| img.style = 'max-width: 100%; border-radius: 8px; margin-top: 10px;'; |
| messageDiv.appendChild(img); |
| } |
| |
| |
| messageDiv.innerHTML += `<strong>${msg.sender}</strong><br>${msg.content || ''}`; |
| messageDiv.addEventListener('click', () => { |
| replyTo = msg.content; |
| replyPreview.textContent = `Replying to: ${msg.content}`; |
| replyPreview.style.display = 'block'; |
| }); |
| |
| chatBox.appendChild(messageDiv); |
| chatBox.scrollTop = chatBox.scrollHeight; |
| } |
| |
| |
| async function loadMessages() { |
| try { |
| const res = await fetch(`/api/messages/${jid}`, { |
| headers: { Authorization: `Bearer ${userKey}` }, |
| }); |
| if (!res.ok) throw new Error('Failed to load messages'); |
| const messages = await res.json(); |
| chatBox.innerHTML = ''; |
| messages.forEach(addMessageToChat); |
| } catch (error) { |
| console.error(error); |
| chatBox.innerHTML = '<p style="color: red;">Failed to load messages.</p>'; |
| } |
| } |
| |
| |
| sendMessageButton.addEventListener('click', async () => { |
| const message = messageInput.value.trim(); |
| if (!message) return; |
| |
| try { |
| await fetch('/send-message', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| Authorization: `Bearer ${userKey}`, |
| }, |
| body: JSON.stringify({ jid, message, replyTo }), |
| }); |
| messageInput.value = ''; |
| replyTo = null; |
| replyPreview.style.display = 'none'; |
| } catch (error) { |
| console.error('Failed to send message:', error); |
| } |
| }); |
| |
| sendImageButton.addEventListener('click', () => { |
| imageInput.click(); |
| }); |
| |
| imageInput.addEventListener('change', async () => { |
| const file = imageInput.files[0]; |
| if (!file) return; |
| |
| const formData = new FormData(); |
| formData.append('image', file); |
| formData.append('jid', jid); |
| formData.append('caption', messageInput.value.trim()); |
| |
| try { |
| const res = await fetch('/upload-image', { |
| method: 'POST', |
| headers: { |
| Authorization: `Bearer ${userKey}`, |
| }, |
| body: formData, |
| }); |
| |
| if (res.ok) { |
| imageInput.value = ''; |
| messageInput.value = ''; |
| replyTo = null; |
| replyPreview.style.display = 'none'; |
| } else { |
| const errorText = await res.text(); |
| console.error('Failed to send image:', errorText); |
| alert(`Error: ${errorText}`); |
| } |
| } catch (error) { |
| console.error('Failed to send image:', error); |
| alert('An error occurred while sending the image.'); |
| } |
| }); |
| |
| |
| if (userKey) { |
| verifyKey(userKey).then((isValid) => { |
| if (isValid) { |
| keyModal.style.display = 'none'; |
| loadMessages(); |
| } else { |
| localStorage.removeItem('userKey'); |
| } |
| }); |
| } |
| </script> |
| </body> |
| </html> |