summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--index.js26
-rw-r--r--static/index.html2
-rw-r--r--static/style.css52
3 files changed, 53 insertions, 27 deletions
diff --git a/index.js b/index.js
index dae7762..d5de68d 100644
--- a/index.js
+++ b/index.js
@@ -3,7 +3,7 @@ const {promises: {readFile}, readFileSync, createReadStream, createWriteStream}
 const {createServer} = require('node:http')
 const {createHash, randomBytes} = require('node:crypto')
 const Readline = require('node:readline');
-const { stdin, stdout } = require('node:process');
+const {stdin, stdout} = require('node:process');
 const {EventEmitter} = require('node:events')
 
 const readline = Readline.createInterface({
@@ -176,7 +176,7 @@ async function handleGetPage(req, res) {
     currentUsers[userId] = nonce
     setTimeout(() => {
       if (currentUsers[userId] === nonce) {
-        chatEvents.emit('event', {type: 'part', userId, args: {nick}})
+        chatEvents.emit('event', {timestamp: Date.now(), type: 'part', userId, args: {nick}})
         delete currentUsers[userId]
       }
     }, 3000)
@@ -187,7 +187,7 @@ async function handleGetPage(req, res) {
   res.on('finish', onDisconnect)
 
   if (!(userId in currentUsers)) {
-    chatEvents.emit('event', {type: 'join', userId, args: {nick}})
+    chatEvents.emit('event', {timestamp: Date.now(), type: 'join', userId, args: {nick}})
   }
   currentUsers[userId] = true
 }
@@ -219,7 +219,7 @@ async function handleMessagePost(req, res) {
   const messageId = randomBytes(20).toString('hex').slice(0, 7)
 
   if (content)
-    chatEvents.emit('event', {type: 'message', userId, args: {messageId, content, nick}})
+    chatEvents.emit('event', {timestamp: Date.now(), type: 'message', userId, args: {messageId, content, nick}})
 
   res.writeHead(303, {location: '/'}).end()
 }
@@ -231,7 +231,7 @@ async function handleDeletePost(req, res) {
   const userId = getUserId(pass)
 
   if (messageId)
-    chatEvents.emit('event', {type: 'delete', userId, args: {messageId}})
+    chatEvents.emit('event', {timestamp: Date.now(), type: 'delete', userId, args: {messageId}})
 
   res.writeHead(303, {location: '/'}).end()
 }
@@ -244,7 +244,7 @@ async function handleEditPost(req, res) {
   const userId = getUserId(pass)
 
   if (messageId && content)
-    chatEvents.emit('event', {type: 'edit', userId, args: {messageId, content}})
+    chatEvents.emit('event', {timestamp: Date.now(), type: 'edit', userId, args: {messageId, content}})
 
   res.writeHead(303, {location: '/'}).end()
 }
@@ -262,16 +262,17 @@ async function handleSavePost(req, res) {
   writeCookie(res, 'semi-pass', newPass)
 
   if ((oldNick && oldNick !== newNick))
-    chatEvents.emit('event', {type: 'nickChange', userId: oldUserId, args: {oldNick, newNick, oldUserId, newUserId}})
+    chatEvents.emit('event', {timestamp: Date.now(), type: 'nickChange', userId: oldUserId, args: {oldNick, newNick, oldUserId, newUserId}})
 
   res.writeHead(303, {location: '/'}).end()
 }
 
-function renderEvent({type, userId: eventUserId, args}, {userId: currentUserId, live}) {
+function renderEvent({type, timestamp, userId: eventUserId, args}, {userId: currentUserId, live}) {
   switch (type) {
     case 'message':
       return `
         <div class="message${(currentUserId === eventUserId) ? ' currentUser' : ''}" data-user-id="${sanitizeText(eventUserId)}" data-message-id="${sanitizeText(args.messageId)}">
+          ${timestamp ? `<span class="time" title="(all times in UTC)">${(new Date(timestamp)).toLocaleTimeString('en-US', {timeZone: 'UTC', hour12: false, hour: '2-digit', minute: '2-digit'})}</span>`: ''}
           <span class="nick" title="${sanitizeText(eventUserId)}">${sanitizeText(args.nick)}</span>
           <span class="content"><span class="orig">${sanitizeText(args.content)}</span></span>
           ${(currentUserId === eventUserId) ? `<div class="actions">
@@ -327,19 +328,22 @@ function renderEvent({type, userId: eventUserId, args}, {userId: currentUserId,
     case 'nickChange':
       return `
         <div class="nickChange">
-          (<span class="nick" title="${sanitizeText(args.oldUserId)}">${sanitizeText(args.oldNick)}</span> changed name to <span class="new" title="${sanitizeText(args.newUserId)}">${sanitizeText(args.newNick)}</span>)
+          ${timestamp ? `<span class="time" title="(all times in UTC)">${(new Date(timestamp)).toLocaleTimeString('en-US', {timeZone: 'UTC', hour12: false, hour: '2-digit', minute: '2-digit'})}</span>`: ''}
+          <span class="sysmessage">(<span class="nick" title="${sanitizeText(args.oldUserId)}">${sanitizeText(args.oldNick)}</span> changed name to <span class="new" title="${sanitizeText(args.newUserId)}">${sanitizeText(args.newNick)}</span>)</span>
         </div>`
 
     case 'join':
       return `
         <div class="join">
-          (<span class="nick" title="${sanitizeText(eventUserId)}">${sanitizeText(args.nick)}</span> joined the chat)
+          ${timestamp ? `<span class="time" title="(all times in UTC)">${(new Date(timestamp)).toLocaleTimeString('en-US', {timeZone: 'UTC', hour12: false, hour: '2-digit', minute: '2-digit'})}</span>`: ''}
+          <span class="sysmessage">(<span class="nick" title="${sanitizeText(eventUserId)}">${sanitizeText(args.nick)}</span> joined the chat)</span>
         </div>`
 
     case 'part':
       return `
         <div class="part">
-          (<span class="nick" title="${sanitizeText(eventUserId)}">${sanitizeText(args.nick)}</span> left the chat)
+          ${timestamp ? `<span class="time" title="(all times in UTC)">${(new Date(timestamp)).toLocaleTimeString('en-US', {timeZone: 'UTC', hour12: false, hour: '2-digit', minute: '2-digit'})}</span>`: ''}
+          <span class="sysmessage">(<span class="nick" title="${sanitizeText(eventUserId)}">${sanitizeText(args.nick)}</span> left the chat)</span>
         </div>`
   }
 }
diff --git a/static/index.html b/static/index.html
index b411bb2..77718dc 100644
--- a/static/index.html
+++ b/static/index.html
@@ -16,7 +16,7 @@
       </form>
     </div>
     <div id="footer">
-      <p>created by tempest-vi</p>
+      <p>created by tempest</p>
       <button type="button" popovertarget="settings_modal" popovertargetaction="toggle">Settings</button>
       <dialog popover id="settings_modal">
         <iframe width="100%" height="100%" style="border: none;" src="/settings"></iframe>
diff --git a/static/style.css b/static/style.css
index 9957ac9..7f50b68 100644
--- a/static/style.css
+++ b/static/style.css
@@ -23,17 +23,25 @@ body {
 body > div#messages {
   order: -1;
   display: grid;
-  grid-template-columns: fit-content(120px) 1fr fit-content(80px);
+  grid-template-columns: fit-content(60px) fit-content(120px) 1fr fit-content(80px);
 }
 
 .message {
   display: contents;
 }
 
-.message .nick {
+.message .time {
+  grid-column: 1 / 2;
   padding: 4px 8px;
   padding-left: 16px;
-  grid-column: 1 / 2;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.message .nick {
+  padding: 4px 8px;
+  grid-column: 2 / 3;
   overflow-x: hidden;
   text-wrap: nowrap;
   text-overflow: ellipsis;
@@ -42,13 +50,13 @@ body > div#messages {
 }
 
 .message .content {
-  grid-column: 2 / 4;
+  grid-column: 3 / 5;
   padding: 4px 8px;
   align-content: center;
 }
 
 .message:has(.actions) .content {
-  grid-column: 2 / 3;
+  grid-column: 3 / 4;
 }
 
 .message .actions {
@@ -57,7 +65,7 @@ body > div#messages {
   flex-direction: row;
   justify-content: end;
   align-items: center;
-  grid-column: 3 / 4;
+  grid-column: 4 / 5;
   align-content: center;
 }
 
@@ -89,20 +97,34 @@ body > div#messages {
     /* background: #def6ff; */
   }
 
-  & > .nick {
+  & > .time{
     border-left: solid 6px #88dbfb;
     padding-left: 10px;
   }
 }
 
-.nickChange,
-.join,
-.part {
-  grid-column: 1 / 4;
-  opacity: .4;
-  padding: 4px 16px;
-  background: #d3d3d342;
-  font-size: .95em;
+#messages *:has(> .sysmessage) {
+  display: contents;
+
+  .time {
+    grid-column: 1 / 2;
+    background: #d3d3d342;
+    font-size: .95em;
+    padding: 4px 8px;
+    padding-left: 16px;
+    opacity: .4;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+  }
+
+  .sysmessage {
+    grid-column: 2 / 5;
+    background: #d3d3d342;
+    font-size: .95em;
+    padding: 4px 8px;
+    opacity: .4;
+  }
 }
 
 #chat {