add database to chatbox
This commit is contained in:
parent
cb8eb2689c
commit
112f2b57c3
14 changed files with 77 additions and 104 deletions
|
@ -1,17 +1,19 @@
|
|||
import json
|
||||
|
||||
from channels.generic.websocket import AsyncWebsocketConsumer
|
||||
from .models import Message
|
||||
|
||||
from judge.models.profile import Profile
|
||||
|
||||
|
||||
class ChatConsumer(AsyncWebsocketConsumer):
|
||||
async def connect(self):
|
||||
self.room_name = 'common'
|
||||
self.room_name = 'room'
|
||||
self.room_group_name = 'chat_%s' % self.room_name
|
||||
|
||||
# Join room group
|
||||
await self.channel_layer.group_add(
|
||||
self.room_group_name,
|
||||
self.channel_name,
|
||||
self.channel_name
|
||||
)
|
||||
|
||||
await self.accept()
|
||||
|
@ -20,7 +22,7 @@ class ChatConsumer(AsyncWebsocketConsumer):
|
|||
# Leave room group
|
||||
await self.channel_layer.group_discard(
|
||||
self.room_group_name,
|
||||
self.channel_name,
|
||||
self.channel_name
|
||||
)
|
||||
|
||||
# Receive message from WebSocket
|
||||
|
@ -40,8 +42,23 @@ class ChatConsumer(AsyncWebsocketConsumer):
|
|||
# Receive message from room group
|
||||
async def chat_message(self, event):
|
||||
message = event['message']
|
||||
|
||||
time = save_data_and_get_time(message)
|
||||
message['time'] = format_time(time)
|
||||
# Send message to WebSocket
|
||||
await self.send(text_data=json.dumps({
|
||||
'message': message,
|
||||
}))
|
||||
|
||||
|
||||
# return time
|
||||
def save_data_and_get_time(message):
|
||||
new_message = Message(body=message['body'],
|
||||
author=Profile.objects
|
||||
.get(id=message['author_id']),
|
||||
)
|
||||
new_message.save()
|
||||
return new_message.time
|
||||
|
||||
|
||||
def format_time(time):
|
||||
return time.strftime('%H:%M %p %d-%m-%Y')
|
|
@ -1,5 +1,3 @@
|
|||
# based on https://github.com/narrowfail/django-channels-chat
|
||||
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
from django.db import models
|
||||
|
@ -18,19 +16,10 @@ class Message(models.Model):
|
|||
time = models.DateTimeField(verbose_name=_('posted time'), auto_now_add=True)
|
||||
body = models.TextField(verbose_name=_('body of comment'), max_length=8192)
|
||||
|
||||
def notify_ws_clients(self):
|
||||
notification = {
|
||||
'type': 'recieve_group_message',
|
||||
'message': '{}'.format(self.id)
|
||||
}
|
||||
channel_layer = get_channel_layer()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
new_message = self.id
|
||||
self.body = self.body.strip()
|
||||
super(Message, self).save(*args, **kwargs)
|
||||
if new_message is None:
|
||||
self.notify_ws_clients()
|
||||
|
||||
class Meta:
|
||||
app_label = 'chat_box'
|
||||
|
|
|
@ -1,31 +1,17 @@
|
|||
from django.http import HttpResponseRedirect
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
from .models import Message
|
||||
|
||||
|
||||
class ChatView(ListView):
|
||||
model = Message
|
||||
context_object_name = 'messages'
|
||||
context_object_name = 'message'
|
||||
template_name = 'chat/chat.html'
|
||||
title = _('Chat Box')
|
||||
paginate_by = 10
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ChatView, self).get_context_data(**kwargs)
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = self.title
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
return None
|
||||
|
||||
|
||||
def send(request):
|
||||
new_message = Message(body=request.POST['message'],
|
||||
author=request.profile,
|
||||
time=timezone.now())
|
||||
new_message.save()
|
||||
return HttpResponseRedirect(reverse('chat'))
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import chat_box.routing
|
||||
from channels.auth import AuthMiddlewareStack
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
|
||||
import chat_box.routing
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
# (http->django views is added by default)
|
||||
'websocket': AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
chat_box.routing.websocket_urlpatterns,
|
||||
),
|
||||
chat_box.routing.websocket_urlpatterns
|
||||
)
|
||||
),
|
||||
})
|
|
@ -503,6 +503,8 @@ TESTCASE_VISIBLE_LENGTH = 60
|
|||
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240
|
||||
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440
|
||||
|
||||
MESSAGES_TO_LOAD = 15
|
||||
|
||||
ASGI_APPLICATION = 'dmoj.routing.application'
|
||||
CHANNEL_LAYERS = {
|
||||
'default': {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from chat_box.views import ChatView, send
|
||||
from chat_box.views import ChatView
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
|
@ -10,6 +10,8 @@ from django.urls import reverse
|
|||
from django.utils.functional import lazystr
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import RedirectView
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
|
||||
from judge.feed import AtomBlogFeed, AtomCommentFeed, AtomProblemFeed, BlogFeed, CommentFeed, ProblemFeed
|
||||
from judge.forms import CustomAuthenticationForm
|
||||
|
@ -25,7 +27,6 @@ from judge.views.select2 import AssigneeSelect2View, CommentSelect2View, Contest
|
|||
ContestUserSearchSelect2View, OrganizationSelect2View, ProblemSelect2View, TicketUserSelect2View, \
|
||||
UserSearchSelect2View, UserSelect2View
|
||||
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
register_patterns = [
|
||||
|
@ -368,8 +369,8 @@ urlpatterns = [
|
|||
url(r'^custom_checker_sample/', about.custom_checker_sample, name='custom_checker_sample'),
|
||||
|
||||
url(r'^chat/', include([
|
||||
url(r'^$', ChatView.as_view(), name='chat'),
|
||||
url(r'send$', send, name='send_message')
|
||||
url(r'^$', login_required(ChatView.as_view()), name='chat'),
|
||||
|
||||
])),
|
||||
]
|
||||
|
||||
|
|
|
@ -219,7 +219,7 @@ header {
|
|||
|
||||
#nav-shadow {
|
||||
height: 2px;
|
||||
background: linear-gradient($widget_black, transparent);
|
||||
background: linear-gradient(#63c8f9, transparent);
|
||||
}
|
||||
|
||||
#nav-container {
|
||||
|
@ -281,7 +281,7 @@ nav {
|
|||
height: 18px;
|
||||
|
||||
&:link {
|
||||
color: #FFF;
|
||||
color: #9c3706;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 24 KiB |
|
@ -1,16 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="160" height="44">
|
||||
<g transform="matrix(.08265 0 0 -.0827 -18.736 55.375)">
|
||||
<path fill="#FCDB05" stroke="#FCDB05" stroke-width="10" d="M477.898 612.007c-107.844-.25-215.89-92.284-206.703-227.507 6.614-97.33 93.848-202.26 227.87-192.475 101.47 7.41 194.44 95.456 192.42 213.653-1.96 114.6-97.744 208.65-213.587 206.33m3.533-437.256c-123.62-2.002-228.5 102.77-227.64 227.46.87 125.882 103.18 228.238 229.38 226.488 124.6-1.727 225.85-98.733 224.96-230.748-.82-122.186-101.1-223.51-226.69-223.2"/>
|
||||
<path fill="#fcdb05" d="M515.264 390.9c14.44-20.215 31.333-37.793 47.34-56.057 18.196-20.76 18.438-20.43-3.845-36.05-7.16-5.014-14.16-10.288-20.88-15.868-6.27-5.203-10.51-6.375-15.06 2.458-12.79 24.824-43.17 77.232-46.028 76.716-2.73-.5-32.438-53.09-46.36-77.37-4.567-7.97-8.044-6.92-14.116-1.95-10.807 8.84-21.825 17.57-33.59 25.03-7.504 4.75-7.585 8.08-2.073 14.2 18.23 20.26 57.116 65.45 59.423 68.69-28.8 5.974-56.39 11.873-84.065 17.355-10.37 2.055-13.786 4.4-9.2 16.78 4.32 11.65 10.353 23.38 11.56 36.02 1.067 11.187 6.41 11.39 14.473 7.76 24.793-11.16 77.644-34.635 80.63-35.575 1.073 21.123-7.233 79.865-8.628 88.975-1.027 6.686 1.18 9.43 8.496 9.157 14.638-.545 29.38-1.074 43.94.132 11.26.932 13.05-4.163 11.42-12.94-5.17-27.87-5.52-56.143-7.755-85.808 25.865 11.322 51.147 21.774 75.85 33.45 14 6.613 16.357 6.306 19.824-7.96 2.892-11.905 7.53-23.366 11.466-34.877 4.11-12.01-1.486-13.28-9.816-15.007-27.35-5.66-54.684-11.38-83.012-17.28"/>
|
||||
</g>
|
||||
<g transform="matrix(.53656 0 0 .52273 46.09 -512.12)">
|
||||
<path fill="#FCDB05" d="M1.8 1045v-46.4h16.4q24.7 0 24.7 22.6 0 10.8-6.8 17.3-6.7 6.5-18 6.5H1.7zm10.4-37.9v29.4h5.2q6.8 0 10.6-4.1 3.9-4.1 3.9-11.1 0-6.6-3.8-10.4-3.8-3.8-10.7-3.8h-5.1z"/>
|
||||
<path fill="#ffde05" d="M101.5 1045H91.2v-27.8q0-4.5.4-9.9h-.3q-.8 4.3-1.5 6.1l-10.9 31.5h-8.5l-11.1-31.2q-.5-1.3-1.5-6.5h-.3q.4 6.9.4 12v25.7h-9.4v-46.4h15.3l9.5 27.5q1.1 3.3 1.6 6.6h.2q.9-3.8 1.8-6.7l9.5-27.4h14.9v46.4z"/>
|
||||
<g fill="#ffde05">
|
||||
<path d="M113.6 1018.2q-2.5 0-4.2-1.5-1.7-1.6-1.7-3.8 0-2.3 1.7-3.8t4.3-1.5q2.6 0 4.3 1.5 1.7 1.5 1.7 3.8 0 2.4-1.7 3.8-1.7 1.5-4.4 1.5zm0 23.7q-2.5 0-4.2-1.6-1.7-1.6-1.7-3.8 0-2.3 1.7-3.8t4.3-1.5q2.6 0 4.3 1.5 1.7 1.5 1.7 3.8 0 2.4-1.7 3.9t-4.4 1.5z"/>
|
||||
<path d="M129.6 1018.2q-2.5 0-4.2-1.5-1.7-1.6-1.7-3.8 0-2.3 1.7-3.8t4.3-1.5q2.6 0 4.3 1.5 1.7 1.5 1.7 3.8 0 2.4-1.7 3.8-1.7 1.5-4.4 1.5zm0 23.7q-2.5 0-4.2-1.6-1.7-1.6-1.7-3.8 0-2.3 1.7-3.8t4.3-1.5q2.6 0 4.3 1.5 1.7 1.5 1.7 3.8 0 2.4-1.7 3.9t-4.4 1.5z"/>
|
||||
</g>
|
||||
<path fill="#aaa" d="M161.7 1045.8q-10 0-16.2-6.5-6.3-6.5-6.3-16.9 0-11 6.4-17.8 6.4-6.8 16.9-6.8 9.9 0 16 6.5t6.1 17.1q0 10.9-6.4 17.6-6.3 6.7-16.6 6.7zm.5-39q-5.5 0-8.7 4.1-3.2 4.1-3.2 10.9 0 6.9 3.2 10.9t8.5 4q5.4 0 8.6-3.9 3.2-3.9 3.2-10.8 0-7.2-3.1-11.2-3.1-4-8.4-4z"/>
|
||||
<path fill="#aaa" d="M209.6 1026.6q0 9.3-4.3 14.3-4.3 4.9-12.5 4.9-3.7 0-6.8-1.3v-9.8q2.7 2 6 2 7.1 0 7.1-10.6v-27.6h10.5v28z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.9 KiB |
|
@ -42,7 +42,7 @@ $table_header_rounding: 6px;
|
|||
|
||||
th {
|
||||
height: 2em;
|
||||
color: #2b333b;
|
||||
color: white;
|
||||
background-color: $widget_black;
|
||||
border-color: #cccccc;
|
||||
border-width: 1px 1px 0 0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
$highlight_blue: white;
|
||||
$widget_black: #63c8f9;//68BBE3, add8e6
|
||||
$widget_black: #00007d;//68BBE3, add8e6
|
||||
$border_gray: #ccc;
|
||||
$background_gray: #ededed;
|
||||
$background_light_gray: #fafafa;
|
||||
|
|
|
@ -419,7 +419,7 @@ ul.select2-selection__rendered {
|
|||
margin: 0 -5px;
|
||||
background: $widget_black;
|
||||
border-radius: $widget_border_radius $widget_border_radius 0 0;
|
||||
color: #2b333b;
|
||||
color: white;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 7px;
|
||||
|
@ -427,7 +427,7 @@ ul.select2-selection__rendered {
|
|||
}
|
||||
|
||||
.sidebox h3 .fa {
|
||||
color: #2b333b;
|
||||
color: white;
|
||||
float: right;
|
||||
margin: 0.2em 0.4em 0 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% if request.user.is_authenticated %}
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block js_media %}
|
||||
<script src="{{ static('libs/jquery.waypoints.min.js')}}"></script>
|
||||
<script src="{{ static('libs/infinite.min.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
var chatSocket = new WebSocket(
|
||||
'ws://' + window.location.host +
|
||||
|
@ -12,15 +12,14 @@
|
|||
chatSocket.onmessage = function(e) {
|
||||
let data = JSON.parse(e.data)
|
||||
data = data['message']
|
||||
console.log(data)
|
||||
loadMessage(data['content'],
|
||||
data['sender'],
|
||||
loadMessage(data['body'],
|
||||
data['author'],
|
||||
data['time'],
|
||||
data['image'])
|
||||
};
|
||||
|
||||
function loadMessage(content, user, time, image) {
|
||||
li = `<li>
|
||||
li = `<li class="infinite-item">
|
||||
<img src="${image}" class="profile-pic">
|
||||
<div class="body-message">
|
||||
<div class="user-time">
|
||||
|
@ -40,34 +39,44 @@
|
|||
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
|
||||
}
|
||||
|
||||
(function init_chatlog() {
|
||||
ul = $('#chat-log')
|
||||
{% for msg in message|reverse %}
|
||||
loadMessage('{{msg.body}}', '{{msg.author}}', '12:00:00', '{{gravatar(msg.author, 32)}}')
|
||||
{% endfor %}
|
||||
})()
|
||||
|
||||
var infinite = new Waypoint.Infinite({
|
||||
element: $('.infinite-container')[0],
|
||||
onBeforePageLoad: function () {
|
||||
$('.loading').show();
|
||||
},
|
||||
onAfterPageLoad: function ($items) {
|
||||
$('.loading').hide();
|
||||
}
|
||||
});
|
||||
|
||||
$("#chat-submit").click(function() {
|
||||
if ($("#chat-input").val().trim()) {
|
||||
let content = $('#chat-input').val().trim();
|
||||
let body = $('#chat-input').val().trim();
|
||||
let img = '{{ gravatar(request.user, 32) }}'
|
||||
|
||||
message = {
|
||||
'content': content,
|
||||
'body': body,
|
||||
'image': img,
|
||||
'time': calcTime(6), // HCM City
|
||||
'sender': '{{ request.user }}'
|
||||
'author': '{{request.profile}}',
|
||||
'author_id': {{request.profile.id}},
|
||||
}
|
||||
|
||||
// $.post("send/", message)
|
||||
chatSocket.send(JSON.stringify({
|
||||
'message': message,
|
||||
}))
|
||||
// $.post('/chat/send', message)
|
||||
'message': message
|
||||
}));
|
||||
|
||||
$('#chat-input').val('').focus();
|
||||
}
|
||||
});
|
||||
|
||||
function calcTime(offset) {
|
||||
utc = new Date().getTime()
|
||||
nd = new Date(utc + (3600000*offset));
|
||||
return nd.toLocaleString();
|
||||
}
|
||||
|
||||
chatSocket.onclose = function(e) {
|
||||
console.error('Chat socket closed unexpectedly');
|
||||
};
|
||||
|
@ -108,29 +117,15 @@
|
|||
{% endblock js_media %}
|
||||
|
||||
{% block body %}
|
||||
{% csrf_token %}
|
||||
<div id="chat-area">
|
||||
<ul id="chat-log">
|
||||
<li>
|
||||
<img src="https://via.placeholder.com/150" class="profile-pic">
|
||||
<div class="body-message">
|
||||
<div class="user-time">
|
||||
<a href="#" class="user">
|
||||
cuom1999
|
||||
</a>
|
||||
<span class="time">12:00:00</span>
|
||||
</div>
|
||||
<span class="message">
|
||||
<span>It’s possible that a request can come in via POST with an empty POST dictionary – if, say, a form is requested via the POST HTTP method but does not include form data. Therefore, you shouldn’t use if request.POST to check for use of the POST method; instead, use if request.method == "POST" (see HttpRequest.method).</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</li>
|
||||
<ul id="chat-log" class="infinite-container">
|
||||
</ul>
|
||||
{{_('Your message')}}
|
||||
|
||||
<textarea rows="6" id="chat-input"></textarea>
|
||||
</div>
|
||||
<button id="chat-submit"> Send </button>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="{{ request.path }}?page={{ message.next_page_number }}">next</a>
|
||||
{% endif %}
|
||||
{% endblock body %}
|
||||
{% endif %}
|
||||
|
|
Loading…
Reference in a new issue