I wrote a Django chat web app that uses ASGI, Django Channels, Daphne and WebSockets.
Around 10 users are using it fine but only 2 have issues.
One says she SOMETIMES has to type her message in the chat form TWICE since the
first time it is sometimes ignored.
Another says SOME chat rooms do not let her type messages but only see other people’s messages.
The issues go away on a different computer but on here IPHONE the issues persist.
What would an iPhone do to make these sporadic issues happen?
Here is the relevant ASGI channels code..
import django.core.asgi
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
APP = django.core.asgi.get_asgi_application()
import bighelp.models
import asgiref.sync
import channels.auth
import channels.routing
import channels.generic.websocket
import django.urls
import json
ROOM = bighelp.models.Room
MESSAGE = bighelp.models.Message
WRAP = asgiref.sync.sync_to_async
class EventHandler(channels.generic.websocket.AsyncWebsocketConsumer):
async def connect(self):
self.room_ = self.scope["url_route"]["kwargs"]["room_"]
await self.channel_layer.group_add(self.room_,
self.channel_name)
await self.accept()
async def disconnect(self, code):
await self.channel_layer.group_discard(self.room_,
self.channel_name)
async def receive(self, text_data):
text = json.loads(text_data)["text"]
user = self.scope["user"]
user_ = f"{user.first_name} {user.last_name}"
room_ = self.scope["url_route"]["kwargs"]["room_"]
room = await WRAP(ROOM.objects.get)(name = room_)
await WRAP(MESSAGE.objects.create)(user = user,
room = room,
text = text)
await self.channel_layer.group_send(self.room_,
{"type" : "send_",
"user_" : user_,
"text" : text})
async def send_(self, event):
text_data = json.dumps({"user_" : event["user_"],
"text" : event["text"]})
await self.send(text_data = text_data)
websocket = [django.urls.re_path(r"ws/chat/(?P<room_>\w+)/$",
EventHandler.as_asgi())]
websocket = channels.routing.URLRouter(websocket)
websocket = channels.auth.AuthMiddlewareStack(websocket)
application = channels.routing.ProtocolTypeRouter({"http" : APP,
"websocket" : websocket})
and here is the template code…
{% extends "html_base" %}
{% block body_elements %}
<div class = "page" id = "room">
<h1>Chat Room - {{alias}}</h1>
<p>
<a class = "pink"
{% if prov %}
href = "/provs_appts_lim?room_={{room_}}"
{% else %}
href = "/main?room_={{room_}}"
{% endif %}>
Back To Main
</a>
</p>
<div id = "chat">
<table id = "chat_table">
{% for m in messages %}
{% with u=m.user.first_name|add:" "|add:m.user.last_name %}
<tr {% if u == user_ %}
class = "user_"
{% elif user_ in m.text %}
class = "user_"
{% elif "Everyone" in m.text %}
class = "user_"
{% endif %}>
<td>[{{m.dt|date:"Y-m-d h:iA"|lower}}]</td>
<td> {{u}}</td>
<td> | </td>
<td>{{m.text|safe}}</td>
</tr>
{% endwith %}
{% endfor %}
</table>
</div>
<p></p>
<input type = "text" id = "box"/>
<button type = "button" id = "btn">Send</button>
<p> </p>
<h2>Available Chat Rooms</h2>
<div
{% with acct_str=acct|stringformat:"s" %}
{% if eo_blink and room_ != "Everyone"|add:acct_str %}
class = "blink"
{% endif %}
{% endwith %}>
Everyone : <a href = "/chat/Everyone{{acct}}?room_={{room_}}">Enter</a>
</div>
{% for e in rbmi %}
<div {% if e.2 and e.1 != room_ %} class = "blink" {% endif %}>
{{e.0}} : <a href = "/chat/{{e.1}}?room_={{room_}}">Enter</a>
</div>
{% endfor %}
<p> </p>
<h2>New Chat Room</h2>
<form action = "." method = "post"> {% csrf_token %}
<div>Participants To Add: {{form.users.errors}}</div>
<p>{{form.users}}</p>
<input type = "submit" value = "Enter">
</form>
<p>
<a class = "pink"
{% if prov %}
href = "/provs_appts_lim?room_={{room_}}"
{% else %}
href = "/main?room_={{room_}}"
{% endif %}>
Back To Main
</a>
</p>
</div>
<script>
chat = document.getElementById("chat");
chat_tab = document.getElementById("chat_table");
btn = document.getElementById("btn");
box = document.getElementById("box");
domain = window.location.host;
ws = new WebSocket(`wss://${domain}/ws/chat/{{room_}}/`);
url_re = /(https:\/\/[^\s]+)/g;
tabs = {{tabs|safe}};
tab = 9;
function dt() {
now = new Date();
year = now.getFullYear();
month = String(now.getMonth() + 1).padStart(2, "0");
day = String(now.getDate()).padStart(2, "0");
hour = now.getHours();
min = now.getMinutes().toString().padStart(2, "0");
ampm = "am";
if (hour >= 12) {ampm = "pm";}
hour = (hour % 12).toString().padStart(2, "0");
if (hour == "00") {hour = "12"};
return `${year}-${month}-${day} ${hour}:${min}${ampm}`;
}
function replace_on_tab(event) {
code = event.keyCode || event.which;
if (code === tab) {
event.preventDefault();
index = box.value.lastIndexOf(" ") + 1;
search = box.value.substring(index);
replace = tabs.find(tab => tab.startsWith(search));
replace += ": ";
box.value = box.value.substring(0, index) + replace;
}
}
function send_box_text(event) {
text = box.value.trim().replace(url_re, function(url) {
return '<a href = "' + url + '">' + url + "</a>";});
if (text !== "") {
ws.send(JSON.stringify({"text": text}));
box.value = "";
}
}
function print_box_text(event) {
message = JSON.parse(event.data);
tr = "<tr>";
if ((message.user_ == "{{user_}}") ||
message.text.includes("{{user_}}") ||
message.text.includes("Everyone")) {
tr = '<tr class = "user_">';
}
chat_tab.innerHTML += `${tr}
<td>[${dt()}]</td>
<td> ${message.user_}</td>
<td> | </td>
<td>${message.text}</td>
</tr>`
chat.scrollTop = chat.scrollHeight;
}
btn.addEventListener("click", send_box_text);
box.addEventListener("keydown", replace_on_tab);
ws.onmessage = print_box_text;
</script>
{% endblock %}