/ client / static / index.html
index.html
  1  <!DOCTYPE html>
  2  <html lang="en">
  3  <head>
  4      <meta charset="UTF-8">
  5      <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6      <title>Amnezichat</title>
  7      <link rel="stylesheet" href="/static/styles.css">
  8      <script src="/static/purify.min.js"></script>
  9      <script>
 10          let profilePicBase64 = "";
 11  
 12          // Handle profile picture upload
 13          function handleProfilePicChange(event) {
 14              const file = event.target.files[0];
 15              if (file) {
 16                  const reader = new FileReader();
 17                  reader.onloadend = function () {
 18                      profilePicBase64 = reader.result;
 19                  };
 20                  reader.readAsDataURL(file);
 21              }
 22          }
 23  
 24          // Handle media upload (image/video)
 25          function handleMediaChange(event) {
 26              const file = event.target.files[0];
 27              if (file) {
 28                  const reader = new FileReader();
 29                  reader.onloadend = async function () {
 30                      const mediaBase64 = reader.result;
 31  
 32                      let mediaMessage = { message: `<media>${mediaBase64}</media>` };
 33                      
 34                      // Include profile picture in media message if available
 35                      if (profilePicBase64) {
 36                          mediaMessage.message = `<pfp>${profilePicBase64}</pfp>` + mediaMessage.message;
 37                      }
 38  
 39                      // Send media as a separate message
 40                      await fetch('/send', {
 41                          method: 'POST',
 42                          headers: { 'Content-Type': 'application/json' },
 43                          body: JSON.stringify(mediaMessage)
 44                      });
 45  
 46                      fetchMessages();
 47                  };
 48                  reader.readAsDataURL(file);
 49              }
 50          }
 51  
 52          // Fetch messages from the server
 53          async function fetchMessages() {
 54              const response = await fetch('/messages');
 55              const messages = await response.json();
 56  
 57              const messageHTML = messages.map(msg => {
 58                  const base64ImagePattern = /^data:image\/(png|jpeg|jpg|gif);base64,/;
 59                  const base64VideoPattern = /^data:video\/(mp4|webm|ogg);base64,/;
 60                  const pfpMatch = msg.match(/<pfp>(.*?)<\/pfp>/);
 61                  const mediaMatch = msg.match(/<media>(.*?)<\/media>/);
 62                  let messageText = msg.replace(/<pfp>.*?<\/pfp>/, '').replace(/<media>.*?<\/media>/, '');
 63                  let profilePic = "";
 64                  let mediaContent = "";
 65  
 66                  // Display profile picture if available
 67                  if (pfpMatch && base64ImagePattern.test(pfpMatch[1])) {
 68                      profilePic = `<img src="${pfpMatch[1]}" alt="Profile Picture" style="width: 50px; height: 50px; border-radius: 50%; margin-right: 10px;">`;
 69                  }
 70  
 71                  // Display media as a separate full-size element
 72                  if (mediaMatch && (base64ImagePattern.test(mediaMatch[1]) || base64VideoPattern.test(mediaMatch[1]))) {
 73                      if (base64ImagePattern.test(mediaMatch[1])) {
 74                          mediaContent = `<div>${profilePic}<img src="${mediaMatch[1]}" alt="Media" style="width: 100%; max-width: 600px; margin-top: 10px;"></div>`;
 75                      } else if (base64VideoPattern.test(mediaMatch[1])) {
 76                          mediaContent = `<div>${profilePic}<video controls style="width: 100%; max-width: 600px; margin-top: 10px;"><source src="${mediaMatch[1]}" type="video/mp4">Your browser does not support the video tag.</video></div>`;
 77                      }
 78                      return mediaContent;  // Media is a separate message
 79                  }
 80  
 81                  return `<div style="display: flex; align-items: center; margin-bottom: 10px;">${profilePic}<p>${DOMPurify.sanitize(messageText)}</p></div>`;
 82              }).join('');
 83  
 84              document.getElementById('messages').innerHTML = messageHTML;
 85              document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight;
 86          }
 87  
 88          // Send text message to the server
 89          async function sendMessage() {
 90              const message = document.getElementById('messageInput').value;
 91              if (message.trim() !== "") {
 92                  let messageData = { message: DOMPurify.sanitize(message) };
 93  
 94                  // Include profile picture in text message if available
 95                  if (profilePicBase64) {
 96                      messageData.message = `<pfp>${profilePicBase64}</pfp>` + messageData.message;
 97                  }
 98  
 99                  await fetch('/send', {
100                      method: 'POST',
101                      headers: { 'Content-Type': 'application/json' },
102                      body: JSON.stringify(messageData)
103                  });
104  
105                  document.getElementById('messageInput').value = '';
106                  fetchMessages();
107              }
108          }
109  
110          function toggleSettings() {
111              const modal = document.getElementById('settings');
112              modal.style.display = modal.style.display === 'flex' ? 'none' : 'flex';
113          }
114  
115          function closeSettings() {
116              document.getElementById('settings').style.display = 'none';
117          }
118      </script>
119  </head>
120  <body onload="fetchMessages()">
121      <div class="container">
122          <div id="messages"></div>
123          <div class="input-container">
124              <input type="text" id="messageInput" placeholder="Type a message..." autocomplete="off">
125              <button id="settingsButton" onclick="toggleSettings()">Settings</button>
126              <button onclick="sendMessage()">Send</button>
127          </div>
128      </div>
129  
130      <div id="settings">
131          <div class="settings-content">
132              <h2>Settings</h2>
133              <!-- Hidden file input for selecting profile picture -->
134              <input type="file" accept="image/*" id="profilePicInput" style="display:none;" onchange="handleProfilePicChange(event)">
135              <!-- Button that opens the file input for profile picture -->
136              <button onclick="document.getElementById('profilePicInput').click()">Choose Profile Picture</button>
137  
138              <!-- Hidden file input for selecting media (image or video) -->
139              <input type="file" accept="image/*,video/*" id="mediaInput" style="display:none;" onchange="handleMediaChange(event)">
140              <!-- Button that opens the file input for media -->
141              <button onclick="document.getElementById('mediaInput').click()">Send Media</button>
142  
143              <button onclick="closeSettings()">Close</button>
144          </div>
145      </div>
146  </body>
147  </html>