-# Generated by Django 5.1.1 on 2024-09-30 15:56
+# Generated by Django 5.1.1 on 2024-10-04 21:43
import django.contrib.auth.models
import django.contrib.auth.validators
),
pgtrigger.migrations.AddTrigger(
model_name='user',
- trigger=pgtrigger.compiler.Trigger(name='pg_notify_user', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","old":{"id":\' || OLD."id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","new":{"id":\' || NEW."id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='2f48ed2d25af6836e62e7506ebb833638d21f225', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_user_152e9', table='chat_user', when='AFTER')),
+ trigger=pgtrigger.compiler.Trigger(name='pg_notify_user', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || OLD."id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || NEW."id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='7f6e8eba01193825febb722ab5558a49773b1c1a', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_user_152e9', table='chat_user', when='AFTER')),
),
pgtrigger.migrations.AddTrigger(
model_name='user',
),
pgtrigger.migrations.AddTrigger(
model_name='channelmessage',
- trigger=pgtrigger.compiler.Trigger(name='pg_notify_channelmessage', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","old":{"id":\' || OLD."id" || \',"user_id":\' || OLD."user_id" || \',"channel_id":\' || OLD."channel_id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","new":{"id":\' || NEW."id" || \',"user_id":\' || NEW."user_id" || \',"channel_id":\' || NEW."channel_id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='8839d39c99595353dad9ba1de4805be117880a3b', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_channelmessage_d2b2e', table='chat_channelmessage', when='AFTER')),
+ trigger=pgtrigger.compiler.Trigger(name='pg_notify_channelmessage', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || OLD."id" || \',"user_id":\' || OLD."user_id" || \',"channel_id":\' || OLD."channel_id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || NEW."id" || \',"user_id":\' || NEW."user_id" || \',"channel_id":\' || NEW."channel_id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='b6e672dd9e454c168ef544260779943b1e40fe6d', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_channelmessage_d2b2e', table='chat_channelmessage', when='AFTER')),
),
pgtrigger.migrations.AddTrigger(
model_name='channelmessage',
),
pgtrigger.migrations.AddTrigger(
model_name='channeluser',
- trigger=pgtrigger.compiler.Trigger(name='pg_notify_channeluser', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","old":{"id":\' || OLD."id" || \',"user_id":\' || OLD."user_id" || \',"channel_id":\' || OLD."channel_id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","new":{"id":\' || NEW."id" || \',"user_id":\' || NEW."user_id" || \',"channel_id":\' || NEW."channel_id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='e919ee13216581d3895b820bfef92e17397b7c1a', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_channeluser_f01cc', table='chat_channeluser', when='AFTER')),
+ trigger=pgtrigger.compiler.Trigger(name='pg_notify_channeluser', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || OLD."id" || \',"user_id":\' || OLD."user_id" || \',"channel_id":\' || OLD."channel_id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || NEW."id" || \',"user_id":\' || NEW."user_id" || \',"channel_id":\' || NEW."channel_id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='6d4422d5ba8d654603affe34f0cabebe53fe23b0', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_channeluser_f01cc', table='chat_channeluser', when='AFTER')),
),
pgtrigger.migrations.AddTrigger(
model_name='channeluser',
),
pgtrigger.migrations.AddTrigger(
model_name='channel',
- trigger=pgtrigger.compiler.Trigger(name='pg_notify_channel', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","old":{"id":\' || OLD."id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","new":{"id":\' || NEW."id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='07e8b07cb0612a98869139e71cda37635e0d4fda', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_channel_fab0c', table='chat_channel', when='AFTER')),
+ trigger=pgtrigger.compiler.Trigger(name='pg_notify_channel', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || OLD."id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || NEW."id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='32baa6dfc09e87012fc270e79ff8a074b5c9ffcd', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_channel_fab0c', table='chat_channel', when='AFTER')),
),
pgtrigger.migrations.AddTrigger(
model_name='channel',
),
pgtrigger.migrations.AddTrigger(
model_name='privatemessage',
- trigger=pgtrigger.compiler.Trigger(name='pg_notify_privatemessage', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","old":{"id":\' || OLD."id" || \',"sender_id":\' || OLD."sender_id" || \',"recipient_id":\' || OLD."recipient_id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","new":{"id":\' || NEW."id" || \',"sender_id":\' || NEW."sender_id" || \',"recipient_id":\' || NEW."recipient_id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='5b1db355e28bdcbe3440d36e8da1709c7cba8156', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_privatemessage_92534', table='chat_privatemessage', when='AFTER')),
+ trigger=pgtrigger.compiler.Trigger(name='pg_notify_privatemessage', sql=pgtrigger.compiler.UpsertTriggerSql(func='\n IF TG_OP = \'DELETE\' THEN\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || OLD."id" || \',"sender_id":\' || OLD."sender_id" || \',"recipient_id":\' || OLD."recipient_id" || \'}}\'\n );\n ELSE\n PERFORM pg_notify(\n \'pg_notify\',\n \'{"op":"\' || TG_OP || \'","table":"\' || TG_TABLE_NAME ||\n \'","obj":{"id":\' || NEW."id" || \',"sender_id":\' || NEW."sender_id" || \',"recipient_id":\' || NEW."recipient_id" || \'}}\'\n );\n END IF;\n RETURN NULL;\n ', hash='03ce1756ef8739ddbaac8c46c954bb7f29cfb352', operation='DELETE OR INSERT OR UPDATE', pgid='pgtrigger_pg_notify_privatemessage_92534', table='chat_privatemessage', when='AFTER')),
),
pgtrigger.migrations.AddTrigger(
model_name='privatemessage',
(function () {
- var current_channel = {};
- var users_per_id = null;
- var channels_per_id = null;
+ var current_channel = null, private_manager = null, channel_manager = null;
- function clear_children(element) {
- while (element.firstChild) {
- element.removeChild(element.firstChild);
- }
+ var bottom_callback, no_bottom_callback;
+ bottom_callback = no_bottom_callback = function () {
+ bottom_callback = no_bottom_callback;
+ };
+
+ function xhr(method, url, callback, data) {
+ var cb = callback
+ ? function () { callback(JSON.parse(this.responseText)); }
+ : null;
+ return _.xhr(
+ method,
+ url,
+ cb,
+ function (msg) { add_msg(null, "xhr_error", msg); },
+ function (msg) { add_msg(null, "xhr_abort", msg); },
+ data,
+ );
}
function get_data_id(element) {
var attr = element.getAttribute("data-id");
- if (attr === null)
- return null;
- return Number(attr);
+ return attr !== null ? Number(attr) : null;
}
function get_current_user_id() {
- return get_data_id(document.getElementsByClassName("user")[0].children[0]);
+ return get_data_id(_.dgEBCN0("user").children[0]);
}
function set_msg(p, sender, msg) {
- clear_children(p);
+ _.clear_children(p);
if (typeof sender === 'number') {
- sender = users_per_id[sender].username;
+ sender = private_manager.items_per_id[sender].username;
}
- p.appendChild(document.createTextNode(sender));
- p.appendChild(document.createTextNode(": "));
- p.appendChild(document.createTextNode(msg));
- }
-
- function add_msg(msg_id, sender, msg) {
- var p = document.createElement("P");
- p.setAttribute("data-id", msg_id);
- set_msg(p, sender, msg);
- document.getElementsByClassName("messages")[0].appendChild(p);
+ p.appendChild(_.dcTN(sender));
+ p.appendChild(_.dcTN(": "));
+ p.appendChild(_.dcTN(msg));
+ return p;
}
- function find_existing_message(msg_id, callback) {
- var i, messages = document.getElementsByClassName("messages")[0];
- for (i = 0; i < messages.children.length; i++) {
- if (get_data_id(messages.children[i]) === msg_id) {
- fetch_data(current_channel.url + msg_id + "/", callback);
- return;
- }
+ function make_tag_with_data_id(tag_name, data_id) {
+ var i, tag = document.createElement(tag_name);
+ if (data_id !== null) {
+ tag.setAttribute("data-id", data_id);
+ }
+ for (i = 2; i < arguments.length; i += 1) {
+ tag.appendChild(arguments[i]);
}
+ return tag;
}
- function delete_existing_message(msg_id) {
- var i, messages = document.getElementsByClassName("messages")[0];
- for (i = 0; i < messages.children.length; i++) {
- if (get_data_id(messages.children[i]) === msg_id) {
- messages.removeChild(messages.children[i]);
- return;
- }
+ function make_callback_a(callback) {
+ var a = document.createElement("a"), i;
+ a.setAttribute("href", "javascript:void(0)");
+ a.addEventListener("click", callback);
+ for (i = 1; i < arguments.length; i += 1) {
+ a.appendChild(arguments[i]);
}
+ return a;
}
- function ws_receive(msg) {
- var data = JSON.parse(msg.data);
- add_msg(null, "ws", msg.data);
- if (
- current_channel.url === "/api/privatemessage/"
- && data.table === "chat_privatemessage"
- ) {
- if (
- (
- data.new.sender_id === get_current_user_id()
- && data.new.recipient_id === current_channel.data.recipient
- ) || (
- data.new.sender_id === current_channel.data.recipient
- && data.new.recipient_id === get_current_user_id()
- )
- ) {
- if (data.op === "INSERT") {
- fetch_data(
- current_channel.url + data.new.id + "/", add_privatemessage
- );
- } else if (data.op === "UPDATE") {
- find_existing_message(data.new.id, update_privatemessage);
- } else if (data.op === "DELETE") {
- delete_existing_message(data.old.id);
- } else if (data.op === "TRUNCATE") {
- clear_children(document.getElementsByClassName("messages")[0]);
- }
- }
- } else if (
- current_channel.url === "/api/channelmessage/"
- && data.table === "chat_channelmessage"
- ) {
- if (data.new.channel_id === current_channel.data.channel) {
- if (data.op === "INSERT") {
- fetch_data(
- current_channel.url + data.new.id + "/", add_channelmessage
- );
- } else if (data.op === "UPDATE") {
- find_existing_message(data.new.id, update_channelmessage);
- } else if (data.op === "DELETE") {
- delete_existing_message(data.old.id);
- } else if (data.op === "TRUNCATE") {
- clear_children(document.getElementsByClassName("messages")[0]);
+ function find_existing_msg(msg_id, callback) {
+ var i, messages = _.dgEBCN0("messages");
+ return _.foreach_arr(
+ messages.children,
+ function (item) {
+ if (get_data_id(item) === msg_id) {
+ callback(item);
+ return true;
}
}
- } else if (data.table === "chat_user") {
- reload_users();
- } else if (data.table === "chat_channel") {
- reload_channels();
- }
- }
-
- function reload_users() {
- clear_children(document.getElementsByClassName("users")[0].children[0]);
- fetch_data("/api/user/", setup_user_callback);
+ );
}
- function reload_channels() {
- clear_children(document.getElementsByClassName("channels")[0].children[0]);
- fetch_data("/api/channel/", setup_channel_callback);
+ function add_msg(id, user, text) {
+ _.dgEBCN0("messages").appendChild(
+ set_msg(make_tag_with_data_id("p", id), user, text)
+ );
}
- function xhr_error(msg) {
- console.log(msg);
- add_msg(null, "xhr_error", msg);
+ function add_msg_callback(item) {
+ add_msg(item.id, item[current_channel.msg_user_field], item.text);
}
- function xhr_abort(msg) {
- console.log(msg);
- add_msg(null, "xhr_abort", msg);
+ function clear_messages_header() {
+ var messages_header = _.dgEBCN0("messages_header");
+ _.clear_children(messages_header.children[0]);
+ _.clear_children(messages_header.children[1]);
+ _.clear_after(messages_header.children[1]);
}
- function fetch_data(url, callback) {
- var xhr = new XMLHttpRequest();
- xhr.addEventListener("load", callback);
- xhr.addEventListener("error", xhr_error);
- xhr.open("get", url);
- xhr.send();
+ function clear_channel() {
+ _.clear_children(_.dgEBCN0("messages"));
+ clear_messages_header();
+ _.dgEBCN0("messages_header").style.display = "none";
+ _.dgEBCN0("messages_footer").children[0].disabled = true;
}
- function post_data(url, data, callback) {
- var xhr = new XMLHttpRequest();
- if (callback !== null) {
- xhr.addEventListener("load", callback);
+ function set_channel(field_value) {
+ clear_channel();
+ if (this === window) {
+ current_channel = null;
+ _.dgEBCN0("messages").appendChild(_.dcTN("No channel selected!"));
+ return;
}
- xhr.addEventListener("error", xhr_error);
- xhr.addEventListener("abort", xhr_abort);
- xhr.open("post", url);
- xhr.send(data);
- }
-
- function add_privatemessage() {
- var msg = JSON.parse(this.responseText);
- add_msg(msg.id, msg.sender, msg.text);
- }
-
- function update_privatemessage() {
- var msg = JSON.parse(this.responseText);
- var i, messages = document.getElementsByClassName("messages")[0];
- for (i = 0; i < messages.children.length; i++) {
- if (get_data_id(messages.children[i]) === msg.id) {
- set_msg(messages.children[i], msg.sender, msg.text);
- return;
+ current_channel = this;
+ this.msg_data[this.msg_dest_field] = field_value;
+ _.dgEBCN0("messages_footer").children[0].disabled = false;
+ current_channel.setup_channel_header();
+ xhr(
+ "get",
+ this.msg_url + "?" + this.msg_dest_field + "=" + field_value.toString(),
+ function (msgs) {
+ var messages = _.dgEBCN0("messages");
+ // add a "load previous" button here
+ _.foreach_arr(msgs.result, add_msg_callback);
+ messages.scrollTop = messages.scrollHeight - messages.clientHeight;
}
- }
+ );
}
- function add_privatemessages() {
- var msgs = JSON.parse(this.responseText), i;
- for (i = 0; i < msgs.result.length; i += 1) {
- add_msg(msgs.result[i].id, msgs.result[i].sender, msgs.result[i].text);
+ function update_channel_header() {
+ if (current_channel !== this) {
+ return;
+ }
+ if (this.items_per_id[this.msg_data.channel_id]) {
+ current_channel.setup_channel_header();
+ } else {
+ set_channel.call(window);
}
}
- function set_privatemessage_channel(user_id) {
- var messages = document.getElementsByClassName("messages")[0];
- current_channel = {
- "url": "/api/privatemessage/",
- "data": {"recipient": user_id},
- }
- clear_children(messages);
- fetch_data(
- "/api/privatemessage/?other=" + user_id.toString(), add_privatemessages
- );
- document.getElementsByClassName("input")[0].children[0].removeAttribute(
- "disabled"
+ function items_reload_callback(item) {
+ var t = this;
+ this.items_per_id[item.id] = item;
+ _.dgEBCN0(this.items_classname).children[0].appendChild(
+ make_tag_with_data_id(
+ "li",
+ item.id,
+ make_callback_a(
+ function () { t.set_channel(item.id); },
+ _.dcTN(this.item_to_str(item)),
+ ),
+ ),
);
}
- function setup_user_callback() {
- var data = JSON.parse(this.responseText), ul, li, a, p;
- ul = document.getElementsByClassName("users")[0].children[0];
- users_per_id = {};
- for (i = 0; i < data.result.length; i += 1) {
- users_per_id[data.result[i].id] = data.result[i];
- if (data.result[i].is_authenticated) {
- p = document.createElement("P");
- p.setAttribute("data-id", data.result[i].id);
- p.appendChild(document.createTextNode(data.result[i].username));
- document.getElementsByClassName("user")[0].appendChild(p);
- }
- li = document.createElement("li");
- li.setAttribute("data-id", data.result[i].id);
- a = document.createElement("a");
- a.setAttribute("href", "#");
- a.appendChild(
- document.createTextNode(
- data.result[i].username + "\xa0(" + data.result[i].email + ")"
- )
+ function PrivateMessageManager() {
+ var t = this;
+ this.msg_url = "/api/privatemessage/";
+ this.msg_table = "chat_privatemessage";
+ this.msg_user_field = "sender_id";
+ this.msg_dest_field = "recipient_id";
+ this.msg_data = {};
+ this.set_channel = set_channel.bind(this);
+ this.setup_channel_header = function () {
+ var messages_header = _.dgEBCN0("messages_header");
+ clear_messages_header();
+ messages_header.children[0].appendChild(
+ _.dcTN(this.item_to_str(this.items_per_id[this.msg_data.recipient_id]))
);
- a.addEventListener(
- "click",
- (function (user_id) {
- return function () { set_privatemessage_channel(user_id); };
- }(data.result[i].id))
+ messages_header.style.display = "block";
+ };
+ this.items_url = "/api/user/";
+ this.items_classname = "users";
+ this.items_per_id = {};
+ this.items_reload = function () {
+ console.log("a");
+ _.clear_children(_.dgEBCN0(this.items_classname).children[0]);
+ _.clear_attributes(this.items_per_id);
+ console.log("b");
+ xhr(
+ "get",
+ this.items_url,
+ function (data) {
+ console.log("items_reload callback", data);
+ _.foreach_arr(
+ data.result,
+ function (item) {
+ items_reload_callback.call(t, item);
+ if (!item.is_authenticated) {
+ return;
+ }
+ _.clear_children(_.dgEBCN0("user"));
+ _.dgEBCN0("user").appendChild(
+ make_tag_with_data_id(
+ "p", item.id, _.dcTN(item.username)
+ ),
+ );
+ }
+ );
+ update_channel_header.call(t);
+ },
);
- li.appendChild(a);
- ul.appendChild(li);
+ console.log("c");
}
+ this.item_to_str = function (item) {
+ return item.username + "\xa0(" + item.email + ")";
+ };
+ this.match_channel = function (data) {
+ var user_id, recipient_id;
+ if (data.table !== this.msg_table) {
+ return false;
+ }
+ user_id = get_current_user_id();
+ other_id = current_channel.msg_data.recipient_id;
+ return (
+ data.obj.sender_id === user_id && data.obj.recipient_id === other_id
+ ) || (
+ data.obj.sender_id === other_id && data.obj.recipient_id === user_id
+ );
+ };
+ this.items_reload();
}
- function add_channelmessage() {
- var msg = JSON.parse(this.responseText);
- add_msg(msg.id, msg.user, msg.text);
+ function ChannelManager() {
+ var t = this;
+ this.msg_url = "/api/channelmessage/";
+ this.msg_table = "chat_channelmessage";
+ this.msg_user_field = "user_id";
+ this.msg_dest_field = "channel_id";
+ this.msg_data = {};
+ this.set_channel = set_channel.bind(this);
+ this.setup_channel_header = function () {
+ var messages_header = _.dgEBCN0("messages_header");
+ var btn = document.createElement("button");
+ clear_messages_header();
+ messages_header.children[0].appendChild(
+ _.dcTN(this.item_to_str(this.items_per_id[this.msg_data.channel_id]))
+ );
+ _.foreach_arr(
+ this.items_per_id[this.msg_data.channel_id].users,
+ function (user_id) {
+ var item = private_manager.items_per_id[user_id];
+ messages_header.children[1].appendChild(
+ make_tag_with_data_id(
+ "li",
+ item.id,
+ make_callback_a(
+ function () { private_manager.set_channel(item.id); },
+ _.dcTN(private_manager.item_to_str(item)),
+ ),
+ ),
+ );
+ },
+ );
+ btn.appendChild(_.dcTN("Users"));
+ messages_header.addEventListener("click", function () {
+ messages_header.children[1].style.display = "none";
+ });
+ btn.addEventListener("click", function (event) {
+ event = event || window.event;
+ if (messages_header.children[1].style.display === "none") {
+ messages_header.children[1].style.display = "block";
+ messages_header.children[1].style.top = (btn.offsetTop + btn.offsetHeight).toString() + "px";
+ } else {
+ messages_header.children[1].style.display = "none";
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ });
+ messages_header.appendChild(btn);
+ messages_header.children[1].style.display = "none";
+ messages_header.style.display = "block";
+ };
+ this.items_url = "/api/channel/";
+ this.items_classname = "channels";
+ this.items_per_id = {};
+ this.items_reload = function () {
+ _.clear_children(_.dgEBCN0(this.items_classname).children[0]);
+ _.clear_attributes(this.items_per_id);
+ xhr(
+ "get",
+ this.items_url,
+ function (data) {
+ _.foreach_arr(data.result, items_reload_callback.bind(t));
+ update_channel_header.call(t);
+ },
+ );
+ }
+ this.item_to_str = function (item) { return item.name; };
+ this.match_channel = function (data) {
+ var obj;
+ if (data.table !== this.msg_table) {
+ return false;
+ }
+ return data.obj.channel_id === current_channel.msg_data.channel_id;
+ };
+ this.items_reload();
}
- function update_channelmessage() {
- var msg = JSON.parse(this.responseText);
- var i, messages = document.getElementsByClassName("messages")[0];
- for (i = 0; i < messages.children.length; i++) {
- if (get_data_id(messages.children[i]) === msg.id) {
- set_msg(messages.children[i], msg.user, msg.text);
- return;
+ function stick_to_bottom(element) {
+ if (element.clientHeight + element.scrollTop === element.scrollHeight) {
+ bottom_callback = function () {
+ element.scrollTop = element.scrollHeight - element.clientHeight;
+ no_bottom_callback();
}
+ } else {
+ no_bottom_callback();
}
}
- function add_channelmessages() {
- var msgs = JSON.parse(this.responseText), i;
- for (i = 0; i < msgs.result.length; i += 1) {
- add_msg(msgs.result[i].id, msgs.result[i].user, msgs.result[i].text);
+ function ws_receive_msg(data) {
+ var messages = _.dgEBCN0("messages");
+ if (data.op === "INSERT") {
+ stick_to_bottom(messages);
+ xhr(
+ "get",
+ current_channel.msg_url + data.obj.id + "/",
+ function (item) {
+ add_msg_callback(item);
+ bottom_callback();
+ }
+ );
+ } else if (data.op === "UPDATE") {
+ stick_to_bottom(messages);
+ find_existing_msg(
+ data.obj.id,
+ function (tag) {
+ stick_to_bottom(messages);
+ xhr(
+ "get",
+ current_channel.msg_url + get_data_id(tag).toString() + "/",
+ function (msg) {
+ set_msg(
+ tag, msg[current_channel.msg_user_field], msg.text
+ );
+ bottom_callback();
+ }
+ );
+ },
+ );
+ } else if (data.op === "DELETE") {
+ find_existing_msg(
+ data.obj.id, function (tag) { tag.parentElement.removeChild(tag); }
+ );
+ } else if (data.op === "TRUNCATE") {
+ _.clear_children(messages);
}
}
- function set_channel(channel_id) {
- var messages = document.getElementsByClassName("messages")[0];
- current_channel = {
- "url": "/api/channelmessage/",
- "data": {"channel": channel_id},
+ function ws_receive(msg) {
+ var data = JSON.parse(msg.data);
+ // here we could add indication for the pm or channel that is being modified
+ if (data.table === "chat_channel" || data.table === "chat_channeluser") {
+ channel_manager.items_reload();
+ } else if (data.table === "chat_user") {
+ private_manager.items_reload();
}
- clear_children(messages);
- fetch_data(
- "/api/channelmessage/?channel=" + channel_id.toString(), add_channelmessages
- );
- document.getElementsByClassName("input")[0].children[0].removeAttribute(
- "disabled"
- );
- }
-
- function setup_channel_callback() {
- var data = JSON.parse(this.responseText), ul, li, a;
- ul = document.getElementsByClassName("channels")[0].children[0];
- channels_per_id = {};
- for (i = 0; i < data.result.length; i += 1) {
- channels_per_id[data.result[i].id] = data.result[i];
- li = document.createElement("li");
- li.setAttribute("data-id", data.result[i].id);
- a = document.createElement("a");
- a.setAttribute("href", "#");
- a.appendChild(document.createTextNode(data.result[i].name));
- a.addEventListener(
- "click",
- (function (channel_id) {
- return function () { set_channel(channel_id); };
- }(data.result[i].id))
- );
- li.appendChild(a);
- ul.appendChild(li);
+ if (current_channel !== null && current_channel.match_channel(data)) {
+ ws_receive_msg(data);
}
}
- function send() {
- var ta = document.getElementsByClassName("input")[0].children[0], data, name;
+ function send_msg() {
+ var ta = _.dgEBCN0("messages_footer").children[0], data, name;
data = {"text": ta.value};
- for (name in current_channel.data) {
- if (current_channel.data.hasOwnProperty(name)) {
- data[name] = current_channel.data[name];
- }
- }
- if (current_channel.url) {
- post_data(current_channel.url, JSON.stringify(data), null);
+ _.foreach_obj(
+ current_channel.msg_data, function (name, value) { data[name] = value; }
+ );
+ if (current_channel) {
+ xhr("post", current_channel.msg_url, null, JSON.stringify(data));
ta.value = "";
}
}
keycode = event.code || event.key;
if (keycode === "Enter" || keycode === "NumpadEnter") {
// todo: make it possible to add newlines using ctrl+enter
- send();
+ send_msg();
event.preventDefault();
event.stopPropagation();
}
window.addEventListener(
"load",
function (event) {
- var ws, schema, input;
+ var ws, schema, messages_footer;
+ messages_footer = _.dgEBCN0("messages_footer");
+ set_channel.call(window);
event = event || window.event;
- input = document.getElementsByClassName("input")[0];
- if (!current_channel.url) {
- input.children[0].setAttribute("disabled", "");
- }
if (event.target.readyState !== "complete") {
return;
}
+ private_manager = new PrivateMessageManager();
+ channel_manager = new ChannelManager();
schema = {"http:": "ws:", "https:": "wss:"}[window.location.protocol];
ws = new WebSocket(schema + "//" + window.location.host + "/");
ws.addEventListener("message", ws_receive);
- input.children[0].addEventListener("keydown", input_onkeydown);
- input.children[0].value = "";
- input.children[1].addEventListener("click", send);
- reload_users();
- reload_channels();
+ messages_footer.children[0].addEventListener("keydown", input_onkeydown);
+ messages_footer.children[0].value = "";
+ messages_footer.children[1].addEventListener("click", send_msg);
}
);
}());
+import json
import sys
-from asyncio import CancelledError, ensure_future
+from asyncio import (
+ CancelledError, ensure_future, get_running_loop, run_coroutine_threadsafe
+)
from contextlib import contextmanager
from functools import partial
from io import BytesIO
elif event['type'] == 'websocket.disconnect':
return
elif event['type'] == 'websocket.receive':
+ # ...maybe make it possible to request data through the ws?
if event['text'] == 'ping':
await send({
'type': 'websocket.send',
@contextmanager
-def listen_notify_handler(conn, callback, loop):
- loop.add_reader(conn.fileno(), partial(conn.execute, "SELECT 1"))
- conn.add_notify_handler(callback)
- conn.execute(f"LISTEN {settings.PG_NOTIFY_CHANNEL}")
+def listen_notify_handler(connection, callback):
+ loop = get_running_loop()
+ loop.add_reader(connection.fileno(), partial(connection.execute, "SELECT 1"))
+ connection.add_notify_handler(callback)
+ connection.execute(f"LISTEN {settings.PG_NOTIFY_CHANNEL}")
try:
yield
finally:
- conn.execute(f"UNLISTEN {settings.PG_NOTIFY_CHANNEL}")
- conn.remove_notify_handler(callback)
- loop.remove_reader(conn.fileno())
+ connection.execute(f"UNLISTEN {settings.PG_NOTIFY_CHANNEL}")
+ connection.remove_notify_handler(callback)
+ loop.remove_reader(connection.fileno())
-def process_triggers(send, notification):
- ensure_future(send({"type": "websocket.send", "text": notification.payload}))
+def filter_trigger_always(coro, data, user, user_channels):
+ ensure_future(coro)
+
+
+def filter_trigger_channelmessage(coro, data, user, user_channels):
+ if data["obj"]["channel_id"] in user_channels:
+ ensure_future(coro)
+ else:
+ coro.close()
+
+
+def get_user_channels(user):
+ return set(user.channels.all().values_list("pk", flat=True))
+
+
+def update_user_channels(coro, loop, user, user_channels):
+ new_channels = get_user_channels(user)
+ old_channels = user_channels - new_channels
+ if not old_channels and not new_channels - user_channels:
+ if coro is not None:
+ coro.close()
+ return
+ user_channels -= old_channels
+ user_channels.update(new_channels)
+ if coro is not None:
+ ensure_future(coro, loop=loop)
+
+
+def filter_trigger_channeluser(coro, data, user, user_channels):
+ op = data["op"]
+ if op == "TRUNCATE":
+ if user_channels:
+ user_channels.clear()
+ ensure_future(coro)
+ else:
+ coro.close()
+ return
+ if data["obj"]["user_id"] != user.pk:
+ filter_trigger_channelmessage(coro, data, user, user_channels)
+ return
+ loop = get_running_loop()
+ run_coroutine_threadsafe(
+ sync_to_async(update_user_channels)(coro, loop, user, user_channels), loop
+ )
+
+
+def filter_trigger_privatemessage(coro, data, user, user_channels):
+ if user.pk in (data["obj"]["sender_id"], data["obj"]["recipient_id"]):
+ ensure_future(coro)
+ else:
+ coro.close()
+
+
+filter_triggers = {
+ "chat_user": filter_trigger_always,
+ "chat_channel": filter_trigger_always,
+ "chat_channelmessage": filter_trigger_channelmessage,
+ "chat_channeluser": filter_trigger_channeluser,
+ "chat_privatemessage": filter_trigger_privatemessage,
+}
+
+
+def process_triggers(send, user, user_channels, notification):
+ data = json.loads(notification.payload)
+ filter_triggers[data["table"]](
+ send({"type": "websocket.send", "text": notification.payload}),
+ data,
+ user,
+ user_channels,
+ )
async def handle_websocket(scope, receive, send):
return
await sync_to_async(connection.connect)()
+ user_channels = await sync_to_async(get_user_channels)(request.user)
with listen_notify_handler(
- connection.connection, partial(process_triggers, send), receive.__self__.loop
+ connection.connection,
+ partial(process_triggers, send, request.user, user_channels),
):
try:
await process_ws(receive, send)