Pārlūkot izejas kodu

add: Chat messages

gugdun 10 mēneši atpakaļ
vecāks
revīzija
d235efb325
6 mainītis faili ar 201 papildinājumiem un 31 dzēšanām
  1. 113 17
      public/css/main.css
  2. BIN
      public/img/back.png
  3. 1 1
      src/db.js
  4. 56 9
      src/routes/index.js
  5. 27 0
      src/views/chat.ejs
  6. 4 4
      src/views/chats.ejs

+ 113 - 17
public/css/main.css

@@ -11,7 +11,7 @@ html {
 body {
     display: flex;
     width: 100%;
-    height: 100vh;
+    height: 100dvh;
 }
 
 .hs {
@@ -106,6 +106,8 @@ body {
 
 .text-input {
     display: block;
+    box-sizing: border-box;
+    padding-left: 8px;
     margin: auto;
     width: 100%;
     aspect-ratio: 4 / 1;
@@ -133,7 +135,7 @@ body {
     height: 100%;
 }
 
-.chats-header {
+.chat-header {
     display: flex;
     flex-direction: row;
     align-items: center;
@@ -147,15 +149,19 @@ body {
     flex-direction: column;
     width: 100%;
     height: 100%;
-    overflow-y: scroll;
+    overflow-y: auto;
 }
 
 .chat-title {
-    margin: auto;
+    margin-right: auto;
     font-size: 18pt;
     font-weight: 700;
 }
 
+.chat-title:first-child {
+    margin-left: 16px;
+}
+
 .chat-item {
     display: flex;
     flex-direction: row;
@@ -179,22 +185,55 @@ body {
 
 .empty-label {
     width: 100%;
-    margin-top: 16px;
+    margin: 16px 0px;
     text-align: center;
     font-size: 16pt;
     font-weight: 500;
 }
 
-.logout {
+.action {
     display: flex;
     justify-content: center;
     align-items: center;
 }
 
-.logout img {
+.action img {
     width: 32px;
     height: 32px;
-    margin: 16px;
+    margin: 0px 16px;
+}
+
+.message-list {
+    display: flex;
+    flex-direction: column-reverse;
+    width: 100%;
+    height: 100%;
+}
+
+.message {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+}
+
+.message-username {
+    margin: 0px 16px 10px 16px;
+    font-size: 14pt;
+    font-weight: 700;
+}
+
+.message-text {
+    margin: 0px 16px 8px 16px;
+    font-size: 16pt;
+    font-weight: 500;
+    color: #eee;
+}
+
+.message-datetime {
+    margin: 0px 16px 16px 16px;
+    font-size: 14pt;
+    font-weight: 500;
+    color: #555;
 }
 
 @media screen and (max-width: 600px) {
@@ -226,7 +265,7 @@ body {
         font-size: 12pt;
     }
 
-    .chats-header {
+    .chat-header {
         height: 48px;
     }
 
@@ -234,6 +273,10 @@ body {
         font-size: 16pt;
     }
 
+    .chat-title:first-child {
+        margin-left: 12px;
+    }
+
     .chat-item {
         height: 40px;
     }
@@ -248,10 +291,25 @@ body {
         font-size: 14pt;
     }
 
-    .logout img {
+    .action img {
         width: 26px;
         height: 26px;
-        margin: 12px;
+        margin: 0px 12px;
+    }
+
+    .message-username {
+        margin: 0px 14px 8px 14px;
+        font-size: 12pt;
+    }
+    
+    .message-text {
+        margin: 0px 14px 6px 14px;
+        font-size: 14pt;
+    }
+    
+    .message-datetime {
+        margin: 0px 14px 14px 14px;
+        font-size: 12pt;
     }
 }
 
@@ -284,7 +342,7 @@ body {
         font-size: 10pt;
     }
 
-    .chats-header {
+    .chat-header {
         height: 44px;
     }
 
@@ -292,6 +350,10 @@ body {
         font-size: 14pt;
     }
 
+    .chat-title:first-child {
+        margin-left: 10px;
+    }
+
     .chat-item {
         height: 36px;
     }
@@ -306,10 +368,25 @@ body {
         font-size: 12pt;
     }
 
-    .logout img {
+    .action img {
         width: 23px;
         height: 23px;
-        margin: 11px;
+        margin: 0px 11px;
+    }
+
+    .message-username {
+        margin: 0px 12px 6px 12px;
+        font-size: 11pt;
+    }
+    
+    .message-text {
+        margin: 0px 12px 4px 12px;
+        font-size: 13pt;
+    }
+    
+    .message-datetime {
+        margin: 0px 12px 12px 12px;
+        font-size: 11pt;
     }
 }
 
@@ -342,7 +419,7 @@ body {
         font-size: 8pt;
     }
 
-    .chats-header {
+    .chat-header {
         height: 40px;
     }
 
@@ -350,6 +427,10 @@ body {
         font-size: 13pt;
     }
 
+    .chat-title:first-child {
+        margin-left: 8px;
+    }
+
     .chat-item {
         height: 32px;
     }
@@ -364,9 +445,24 @@ body {
         font-size: 11pt;
     }
 
-    .logout img {
+    .action img {
         width: 20px;
         height: 20px;
-        margin: 10px;
+        margin: 0px 10px;
+    }
+
+    .message-username {
+        margin: 0px 10px 4px 10px;
+        font-size: 10pt;
+    }
+    
+    .message-text {
+        margin: 0px 10px 2px 10px;
+        font-size: 12pt;
+    }
+    
+    .message-datetime {
+        margin: 0px 10px 10px 10px;
+        font-size: 10pt;
     }
 }

BIN
public/img/back.png


+ 1 - 1
src/db.js

@@ -7,6 +7,6 @@ const db = pgp(process.env.POSTGRES_CONNECTION);
     await db.none("CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username TEXT UNIQUE, hashed_password BYTEA, salt BYTEA )");
     await db.none("CREATE TABLE IF NOT EXISTS chats ( id SERIAL PRIMARY KEY, user1_id INTEGER, user2_id INTEGER, CONSTRAINT user1_fk FOREIGN KEY (user1_id) REFERENCES users (id), CONSTRAINT user2_fk FOREIGN KEY (user2_id) REFERENCES users (id) )")
     await db.none("CREATE TABLE IF NOT EXISTS attachments ( id SERIAL PRIMARY KEY, type TEXT NOT NULL, data BYTEA ) ")
-    await db.none("CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, chat_id INTEGER, user_id INTEGER, attachment_id INTEGER, text TEXT, CONSTRAINT chat_fk FOREIGN KEY (chat_id) REFERENCES chats (id), CONSTRAINT user_fk FOREIGN KEY (user_id) REFERENCES users (id), CONSTRAINT attachment_fk FOREIGN KEY (attachment_id) REFERENCES attachments (id) )")
+    await db.none("CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, chat_id INTEGER, user_id INTEGER, attachment_id INTEGER, text TEXT, timestamp TIMESTAMP, CONSTRAINT chat_fk FOREIGN KEY (chat_id) REFERENCES chats (id), CONSTRAINT user_fk FOREIGN KEY (user_id) REFERENCES users (id), CONSTRAINT attachment_fk FOREIGN KEY (attachment_id) REFERENCES attachments (id) )")
 })();
 module.exports = db;

+ 56 - 9
src/routes/index.js

@@ -7,13 +7,12 @@ const ejs = require("ejs");
 const db = require("../db");
 
 const views = path.join(__dirname, "..", "views");
-
 const router = express.Router();
 
 router.get("/", async (req, res) => {
     if (req.user) {
         try {
-            const chats = await db.any("SELECT * FROM chats WHERE user1_id = $1 or user2_id = $1", [ req.user.id ]);
+            const chats = await db.any("SELECT * FROM chats WHERE user1_id = $1 OR user2_id = $1", [ req.user.id ]);
             const userIds = chats.map((chat) => chat.user1_id == req.user.id ? chat.user2_id : chat.user1_id);
             const users = await db.any("SELECT username FROM users WHERE id IN ($1:csv)", [ userIds ]);
             res.render("layout", {
@@ -23,7 +22,6 @@ router.get("/", async (req, res) => {
                 })
             });
         } catch (err) {
-            console.log(err);
             res.render("layout", {
                 child: await ejs.renderFile(path.join(views, "chats.ejs"), {
                     empty: true,
@@ -38,16 +36,65 @@ router.get("/", async (req, res) => {
     }
 });
 
+router.get("/chat/:chat_id", async (req, res) => {
+    if (req.user) {
+        try {
+            const user = await db.one("SELECT id FROM users WHERE username = $1", [ req.params.chat_id ]);
+            const chat = await db.one("SELECT id FROM chats WHERE (user1_id = $1 AND user2_id = $2) OR (user1_id = $2 AND user2_id = $1)", [ req.user.id, user?.id ]);
+            const messages = await db.any("SELECT username, attachment_id, text, timestamp FROM messages JOIN users ON users.id = user_id WHERE chat_id = $1 ORDER BY messages.timestamp DESC", [ chat?.id ]);
+            res.render("layout", {
+                child: await ejs.renderFile(path.join(views, "chat.ejs"), {
+                    title: req.params.chat_id,
+                    empty: messages.length < 1,
+                    messages: messages.map((message) => {
+                        const date = new Date(message.timestamp);
+                        const day = date.getDate().toString().padStart(2, '0');
+                        const month = date.getMonth().toString().padStart(2, '0');
+                        const hours = date.getHours().toString().padStart(2, '0');
+                        const minutes = date.getMinutes().toString().padStart(2, '0');
+                        const seconds = date.getSeconds().toString().padStart(2, '0');
+                        const format = `${day}.${month}.${date.getFullYear()} ${hours}:${minutes}:${seconds}`;
+                        return {
+                            username: message.username,
+                            text: message.text,
+                            datetime: message.timestamp
+                        }
+                    })
+                })
+            });
+        } catch (err) {
+            console.log(err);
+            res.render("layout", {
+                child: await ejs.renderFile(path.join(views, "chat.ejs"), {
+                    title: req.params.chat_id,
+                    empty: true,
+                    messages: []
+                })
+            });
+        }
+    } else {
+        res.redirect("/login");
+    }
+});
+
 router.get("/login", async (req, res) => {
-    res.render("layout", {
-        child: await ejs.renderFile(path.join(views, "login.ejs"))
-    });
+    if (req.user) {
+        res.redirect("/");
+    } else {
+        res.render("layout", {
+            child: await ejs.renderFile(path.join(views, "login.ejs"))
+        });
+    }
 });
 
 router.get("/register", async (req, res) => {
-    res.render("layout", {
-        child: await ejs.renderFile(path.join(views, "register.ejs"))
-    });
+    if (req.user) {
+        res.redirect("/");
+    } else {
+        res.render("layout", {
+            child: await ejs.renderFile(path.join(views, "register.ejs"))
+        });
+    }
 });
 
 module.exports = router;

+ 27 - 0
src/views/chat.ejs

@@ -0,0 +1,27 @@
+<div class="chats-container">
+    <div class="chat-header">
+        <a href="/" class="action">
+            <img src="/img/back.png" />
+        </a>
+        <span class="chat-title"><%- title %></span>
+    </div>
+    <div class="message-list">
+        <% if (empty) { %>
+            <p class="empty-label">No messages</p>
+        <% } %>
+        <% messages.forEach(function(message) { %>
+            <div class="message">
+                <span class="message-username"><%- message.username %></span>
+                <span class="message-text"><%- message.text %></span>
+                <span class="message-datetime"><%- message.datetime %></span>
+            </div>
+        <% }); %>
+    </div>
+</div>
+<script>
+    var timestamps = document.getElementsByClassName("message-datetime")
+    for (var i = 0; i < timestamps.length; i++) {
+        var timestamp = timestamps[i];
+        timestamp.innerText = new Date(timestamp.innerText).toLocaleString()
+    }
+</script>

+ 4 - 4
src/views/chats.ejs

@@ -1,8 +1,8 @@
 <div class="chats-container">
-    <div class="chats-header">
-        <span class="chat-title">Chats</span>
-        <a href="/logout" class="logout">
-            <img src="/img/logout.png" class="logout" />
+    <div class="chat-header">
+        <span class="chat-title">SvinChats</span>
+        <a href="/logout" class="action">
+            <img src="/img/logout.png" />
         </a>
     </div>
     <div class="chat-list">