]> git.mar77i.info Git - chat/commitdiff
display the ts time on messages.
authormar77i <mar77i@protonmail.ch>
Wed, 6 Nov 2024 13:32:50 +0000 (14:32 +0100)
committermar77i <mar77i@protonmail.ch>
Wed, 6 Nov 2024 13:32:50 +0000 (14:32 +0100)
chat/serializers.py
chat/static/chat/chat.css
chat/static/chat/chat.js
chat/static/chat/underscore.js
chat/templates/chat/chat.html
todo.txt

index f542b315d9bd663d16aeb8da67a13b2803094420..a843b38f1b7ee3d2df6656214fc4dc5818ecbf83 100644 (file)
@@ -34,7 +34,7 @@ class ModelSerializer:
         field = self.model._meta.get_field(field_name)
         value = getattr(instance, field_name)
         if isinstance(field, DateTimeField):
-            value = value.isoformat(")
+            value = value.isoformat(timespec="milliseconds")
         elif isinstance(field, (ManyToManyField, ManyToManyRel)):
             value = [v.pk for v in value.all()]
         return value
index 966a8d5b4d6a95b1131f48d19c247d71d5987753..70de80d01d51679d110f1e22018cfc6de45e988f 100644 (file)
@@ -80,6 +80,13 @@ li {
 }
 
 .messages > p {
-    padding: .25em;
+    padding: .5em;
     border-bottom: 1px solid #444;
 }
+
+.date_span {
+    padding-right: 1em;
+    display: inline-block;
+    text-align: right;
+    width: 4em;
+}
index a7a7a55bca6e0bc75c18f7c3b7810d2239a65aa3..a1198225fcb542ca0651edf1e796e7f64ba6a11b 100644 (file)
             url,
             {
                 "abort": function (msg) {
-                    messages_manager.add_msg(null, "xhr_abort", msg, null);
+                    console.log(msg);
+                    new Message({"text": msg, "user": "xhr_abort"}, null);
                 },
                 "error": function (msg) {
-                    messages_manager.add_msg(null, "xhr_error", msg, null);
+                    console.log(msg);
+                    new Message({"text": msg, "user": "xhr_error"}, null);
                 },
                 "load": function () {
                     var response_data;
@@ -87,7 +89,7 @@
         clear_channel();
         if (this === window) {
             current_channel = null;
-            messages_manager.add_msg(null, null, "No channel selected!", null);
+            new Message({"text": "No channel selected!"}, null);
             return;
         }
         current_channel = this;
         this.items_reload();
     }
 
-    function Message(id, user, text) {
-        this.id = id;
-        this.p = make_tag_with_data_id("p", id);
-        this.update = function (user, text) {
-            this.user = user || this.user;
-            this.text = text || this.text;
+    function Message(init_msg, next) {
+        if (typeof init_msg.id === "number") {
+            this.id = init_msg.id;
+            messages_manager.msgs_by_id[this.id.toString(36)] = this;
+        } else {
+            this.id = null;
+        }
+        this.msg = init_msg;
+        this.p = make_tag_with_data_id("p", init_msg.id);
+        _.dgEBCN0("messages").insertBefore(this.p, next || null);
+        this.remove = function () {
+            this.p.parentElement.removeChild(this.p);
+            if (this.id !== null) {
+                delete messages_manager.msgs_by_id[this.id.toString(36)];
+            }
+        };
+        function get_date_span(ts) {
+            var d = new Date(ts);
+            var date_span = document.createElement("span");
+            date_span.appendChild(_.dcTN(_.strftime(d, "%H:%m")));
+            date_span.setAttribute(
+                "title",
+                _.strftime(d, "%Y-%m-%d %H:%M:%S.%f")
+            );
+            date_span.setAttribute("class", "date_span");
+            return date_span;
+        }
+        this.update = function (msg) {
+            msg = msg || this.msg;
+            if (
+                !msg.hasOwnProperty("user")
+                && current_channel !== null
+                && current_channel.hasOwnProperty("msg_user_field")
+                && msg.hasOwnProperty(current_channel.msg_user_field)
+            ) {
+                msg.user = msg[current_channel.msg_user_field];
+                delete msg[current_channel.msg_user_field];
+            }
             _.clear_children(this.p);
-            if (this.user) {
-                if (typeof this.user === "number") {
-                    user = private_manager.items_per_id[this.user].username;
+            if (msg.ts) {
+                this.p.appendChild(get_date_span(msg.ts));
+                this.p.appendChild(_.dcTN(" "));
+            }
+            if (msg.user) {
+                if (typeof msg.user === "number") {
+                    this.p.appendChild(
+                        _.dcTN(
+                            private_manager.items_per_id[
+                                msg.user
+                            ].username
+                        )
+                    );
                 } else {
-                    user = this.user;
+                    this.p.appendChild(_.dcTN(msg.user));
                 }
-                this.p.appendChild(_.dcTN(user));
                 this.p.appendChild(_.dcTN(": "));
             }
-            this.p.appendChild(_.dcTN(this.text));
+            this.p.appendChild(_.dcTN(msg.text));
+            this.msg = msg;
         };
-        this.update(user, text);
+        this.update();
     }
 
     function MessagesManager() {
         var messages = _.dgEBCN0("messages");
         this.msgs_by_id = {};
-        this.add_msg = function (id, user, text, next) {
-            var msg = new Message(id, user, text);
-            messages.insertBefore(msg.p, next || null);
-            if (id) {
-                this.msgs_by_id[id.toString(36)] = msg;
-            }
-        };
         this.by_id = function (msg_id) {
             return this.msgs_by_id[msg_id.toString(36)] || null;
         };
             _.clear_attributes(this.msgs_by_id);
             _.clear_children(messages);
         };
-        this.remove = function (id) {
-            var msg = this.by_id(id);
-            if (msg) {
-                msg.p.parentElement.removeChild(msg.p);
-                delete this.msgs_by_id[msg.id.toString(36)];
-            }
-        };
         this.previous_button = function (url) {
             var p;
             var button;
         };
         this.load_msgs_callback = function (msgs, next) {
             this.previous_button(msgs.previous);
-            _.foreach_arr(msgs.result, function (item) {
-                messages_manager.add_msg(
-                    item.id,
-                    item[current_channel.msg_user_field],
-                    item.text,
-                    next
-                );
+            _.foreach_arr(msgs.result, function (msg) {
+                new Message(msg, next);
             });
         };
         function no_bottom_callback() {
         var ws_schemas = {"http:": "ws:", "https:": "wss:"};
         var ws_receive_msg = {
             DELETE: function (data) {
-                messages_manager.remove(data.obj.id);
+                var msg = messages_manager.get_by(data.obj.id);
+                if (msg) {
+                    msg.remove();
+                }
             },
             INSERT: function (data) {
                 messages_manager.stick_to_bottom();
                 xhr(
                     "get",
                     current_channel.msg_url + data.obj.id + "/",
-                    function (item) {
-                        messages_manager.add_msg(
-                            item.id,
-                            item[current_channel.msg_user_field],
-                            item.text,
-                            null
-                        );
-                        messages_manager.bottom_callback();
-                    }
+                    ws_receive_msg.insert_callback
                 );
             },
             TRUNCATE: function () {
                 messages_manager.clear();
             },
             UPDATE: function (data) {
-                var msg = messages_manager.by_id(data.obj.id);
-                if (!msg) {
+                if (!messages_manager.by_id(data.obj.id)) {
                     return;
                 }
                 messages_manager.stick_to_bottom();
                     "get",
                     (
                         current_channel.msg_url
-                        + msg.id.toString()
+                        + data.obj.id.toString()
                         + "/"
                     ),
-                    function (data) {
-                        msg.update(
-                            data[current_channel.msg_user_field],
-                            data.text
-                        );
-                        messages_manager.bottom_callback();
-                    }
+                    ws_receive_msg.update_callback
                 );
+            },
+            insert_callback: function (item) {
+                new Message(item, null);
+                messages_manager.bottom_callback();
+            },
+            update_callback: function (data) {
+                console.log(messages_manager.by_id(data.id));
+                messages_manager.by_id(data.id).update(data);
+                messages_manager.bottom_callback();
             }
         };
         var ws = new WebSocket(
         ws.addEventListener("message", ws_receive);
         ws.addEventListener("close", function (event) {
             event = event || window.event;
-            messages_manager.add_msg(null, "ws_close", "Closed", null);
+            new Message({"text": "Closed", "user": "ws_close"}, null);
             // display reconnect button here
         });
         ws.addEventListener("error", function (event) {
             event = event || window.event;
-            messages_manager.add_msg(null, "ws_error", event, null);
+            console.log(event);
+            new Message({"text": event, "user": "ws_error"}, null);
             // display reconnect button here
         });
     }
             function () {
                 ta.value = "";
                 ta.disabled = false;
+                ta.focus();
             },
             JSON.stringify(data)
         );
index 3c074aa4ac22320442afeedc7a583e3f23355a01..88ef7494aec134ed0a3fee2744fcea5c611ef69d 100644 (file)
         );
     }
 
+    function left_pad(s, length, fill) {
+        var i;
+        if (typeof s === "number") {
+            s = s.toString();
+        }
+        for (i = 0; s.length < length; i += 1) {
+            s = fill[i % fill.length] + s;
+        }
+        return s;
+    }
+
+    function intl_dateformat(date, locale, obj) {
+        return new Intl.DateTimeFormat(locale || "en-US", obj).format(date);
+    }
+
+    function get_timezone_offset(date) {
+        var tzo = date.getTimezoneOffset();
+        var sign;
+        if (tzo < 0) {
+            sign = "-";
+            tzo *= -1;
+        } else {
+            sign = "+";
+        }
+        return [
+            sign,
+            left_pad(Math.floor(tzo / 60), 2, "0"),
+            left_pad(Math.floor(tzo % 60), 2, "0")
+        ].join("");
+    }
+
+    function get_timezone(date, locale) {
+        var s = intl_dateformat(date, locale, {"timeZoneName": "short"});
+        var pos = s.indexOf(",");
+        if (pos !== -1) {
+            s = s.substring(pos + 1).trim();
+        }
+        return s;
+    }
+
+    function strftime(date, s, locale) {
+        var pos = s.indexOf("%");
+        var out = [];
+        while (true) {
+            pos = s.indexOf("%");
+            if (pos === -1) {
+                break;
+            }
+            out.push(s.substring(0, pos));
+            switch (s[pos + 1]) {
+            case "a":
+                out.push(intl_dateformat(date, locale, {"weekday": "short"}));
+                break;
+            case "A":
+                out.push(intl_dateformat(date, locale, {"weekday": "long"}));
+                break;
+            case "b":
+                out.push(intl_dateformat(date, locale, {"month": "short"}));
+                break;
+            case "B":
+                out.push(intl_dateformat(date, locale, {"month": "long"}));
+                break;
+            case "c":
+                out.push(date.toLocaleString(date));
+                break;
+            case "d":
+                out.push(left_pad(date.getDate(), 2, "0"));
+                break;
+            case "f":
+                out.push(left_pad(date.getMilliseconds(), 3, "0"));
+                break;
+            case "H":
+                out.push(left_pad(date.getHours(), 2, "0"));
+                break;
+            case "I":
+                if (date.getHours() % 12 === 0) {
+                    out.push("12");
+                } else {
+                    out.push(left_pad(date.getHours() % 12, 2, "0"));
+                }
+                break;
+            // missing: "j"
+            case "m":
+                out.push(left_pad(date.getMonth() + 1, 2, "0"));
+                break;
+            case "M":
+                out.push(left_pad(date.getMinutes(), 2, "0"));
+                break;
+            case "p":
+                if (date.getHours() < 12) {
+                    out.push("AM");
+                } else {
+                    out.push("PM");
+                }
+                break;
+            case "S":
+                out.push(left_pad(date.getSeconds(), 2, "0"));
+                break;
+            // missing: "U"
+            case "w":
+                out.push(date.getDay().toString());
+                break;
+            // missing: "W"
+            case "x":
+                out.push(date.toLocaleDateString());
+                break;
+            case "X":
+                out.push(date.toLocaleTimeString());
+                break;
+            case "y":
+                out.push(left_pad(date.getFullYear() % 100, 2, "0"));
+                break;
+            case "Y":
+                out.push(left_pad(date.getFullYear(), 4, "0"));
+                break;
+            case "z":
+                out.push(get_timezone_offset(date));
+                break;
+            case "Z":
+                out.push(get_timezone(date, locale));
+                break;
+            case "%":
+                out.push(s[pos + 1]);
+                break;
+            default:
+                out.push(s.substring(pos, pos + 2));
+            }
+            s = s.substring(pos + 2);
+        }
+        out.push(s);
+        return out.join("");
+    }
+
     function xhr(method, url, callbacks, data) {
         var request = new XMLHttpRequest();
         if (!callbacks.hasOwnProperty("error")) {
         },
         "foreach_arr": foreach_arr,
         "foreach_obj": foreach_obj,
+        "strftime": strftime,
         "xhr": xhr
     };
 }());
index d830515eed504d4edda34f7aae0b97053405d9d1..251beca56773e0ab643757f57bdec2a588a194a1 100644 (file)
@@ -2,7 +2,6 @@
 {% load static %}
 
 {% block head %}
-    <link rel="stylesheet" href="">
     <link rel="stylesheet" href="{% static 'chat/chat.css' %}">
     <script src="{% static 'chat/underscore.js' %}"></script>
     <script src="{% static 'chat/chat.js' %}"></script>
index 9be9c0a6a04bfee505f27b596534c42ed1e0010b..a82254176bdf666164ee534d7831c6ab42fba777 100644 (file)
--- a/todo.txt
+++ b/todo.txt
@@ -1,9 +1,10 @@
 [ ] display a reconnect button when ws is disconnected
    - if reconnect is successful, reload all loaded messages, channel list and user list
-[ ] show message timestamps and message edit/delete menus
+[ ] show message edit/delete menus
    - privileged users can edit / delete any message
 [ ] edit/delete existing messages
 [ ] new message(s) indicators in left panel
+
 [ ] update address bar per channel #channel:<id> / #user:<id> for now?
 [ ] channel management: channel admin
 [ ] write email to user