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
|
import json
|
||||||
|
|
||||||
from channels.generic.websocket import AsyncWebsocketConsumer
|
from channels.generic.websocket import AsyncWebsocketConsumer
|
||||||
|
from .models import Message
|
||||||
|
|
||||||
|
from judge.models.profile import Profile
|
||||||
|
|
||||||
|
|
||||||
class ChatConsumer(AsyncWebsocketConsumer):
|
class ChatConsumer(AsyncWebsocketConsumer):
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
self.room_name = 'common'
|
self.room_name = 'room'
|
||||||
self.room_group_name = 'chat_%s' % self.room_name
|
self.room_group_name = 'chat_%s' % self.room_name
|
||||||
|
|
||||||
# Join room group
|
# Join room group
|
||||||
await self.channel_layer.group_add(
|
await self.channel_layer.group_add(
|
||||||
self.room_group_name,
|
self.room_group_name,
|
||||||
self.channel_name,
|
self.channel_name
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.accept()
|
await self.accept()
|
||||||
|
@ -20,7 +22,7 @@ class ChatConsumer(AsyncWebsocketConsumer):
|
||||||
# Leave room group
|
# Leave room group
|
||||||
await self.channel_layer.group_discard(
|
await self.channel_layer.group_discard(
|
||||||
self.room_group_name,
|
self.room_group_name,
|
||||||
self.channel_name,
|
self.channel_name
|
||||||
)
|
)
|
||||||
|
|
||||||
# Receive message from WebSocket
|
# Receive message from WebSocket
|
||||||
|
@ -40,8 +42,23 @@ class ChatConsumer(AsyncWebsocketConsumer):
|
||||||
# Receive message from room group
|
# Receive message from room group
|
||||||
async def chat_message(self, event):
|
async def chat_message(self, event):
|
||||||
message = event['message']
|
message = event['message']
|
||||||
|
time = save_data_and_get_time(message)
|
||||||
|
message['time'] = format_time(time)
|
||||||
# Send message to WebSocket
|
# Send message to WebSocket
|
||||||
await self.send(text_data=json.dumps({
|
await self.send(text_data=json.dumps({
|
||||||
'message': message,
|
'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 asgiref.sync import async_to_sync
|
||||||
from channels.layers import get_channel_layer
|
from channels.layers import get_channel_layer
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -18,19 +16,10 @@ class Message(models.Model):
|
||||||
time = models.DateTimeField(verbose_name=_('posted time'), auto_now_add=True)
|
time = models.DateTimeField(verbose_name=_('posted time'), auto_now_add=True)
|
||||||
body = models.TextField(verbose_name=_('body of comment'), max_length=8192)
|
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):
|
def save(self, *args, **kwargs):
|
||||||
new_message = self.id
|
new_message = self.id
|
||||||
self.body = self.body.strip()
|
self.body = self.body.strip()
|
||||||
super(Message, self).save(*args, **kwargs)
|
super(Message, self).save(*args, **kwargs)
|
||||||
if new_message is None:
|
|
||||||
self.notify_ws_clients()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'chat_box'
|
app_label = 'chat_box'
|
||||||
|
|
|
@ -4,4 +4,4 @@ from . import consumers
|
||||||
|
|
||||||
websocket_urlpatterns = [
|
websocket_urlpatterns = [
|
||||||
re_path(r'ws/chat/', consumers.ChatConsumer),
|
re_path(r'ws/chat/', consumers.ChatConsumer),
|
||||||
]
|
]
|
|
@ -1,31 +1,17 @@
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
|
|
||||||
from .models import Message
|
from .models import Message
|
||||||
|
|
||||||
|
|
||||||
class ChatView(ListView):
|
class ChatView(ListView):
|
||||||
model = Message
|
model = Message
|
||||||
context_object_name = 'messages'
|
context_object_name = 'message'
|
||||||
template_name = 'chat/chat.html'
|
template_name = 'chat/chat.html'
|
||||||
title = _('Chat Box')
|
title = _('Chat Box')
|
||||||
|
paginate_by = 10
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(ChatView, self).get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['title'] = self.title
|
context['title'] = self.title
|
||||||
return context
|
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.auth import AuthMiddlewareStack
|
||||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||||
|
import chat_box.routing
|
||||||
|
|
||||||
application = ProtocolTypeRouter({
|
application = ProtocolTypeRouter({
|
||||||
# (http->django views is added by default)
|
# (http->django views is added by default)
|
||||||
'websocket': AuthMiddlewareStack(
|
'websocket': AuthMiddlewareStack(
|
||||||
URLRouter(
|
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_NUMBER_FIELDS = 10240
|
||||||
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440
|
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440
|
||||||
|
|
||||||
|
MESSAGES_TO_LOAD = 15
|
||||||
|
|
||||||
ASGI_APPLICATION = 'dmoj.routing.application'
|
ASGI_APPLICATION = 'dmoj.routing.application'
|
||||||
CHANNEL_LAYERS = {
|
CHANNEL_LAYERS = {
|
||||||
'default': {
|
'default': {
|
||||||
|
@ -511,4 +513,4 @@ CHANNEL_LAYERS = {
|
||||||
"hosts": [('0.0.0.0', 6379)],
|
"hosts": [('0.0.0.0', 6379)],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -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 import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -10,6 +10,8 @@ from django.urls import reverse
|
||||||
from django.utils.functional import lazystr
|
from django.utils.functional import lazystr
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.generic import RedirectView
|
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.feed import AtomBlogFeed, AtomCommentFeed, AtomProblemFeed, BlogFeed, CommentFeed, ProblemFeed
|
||||||
from judge.forms import CustomAuthenticationForm
|
from judge.forms import CustomAuthenticationForm
|
||||||
|
@ -25,7 +27,6 @@ from judge.views.select2 import AssigneeSelect2View, CommentSelect2View, Contest
|
||||||
ContestUserSearchSelect2View, OrganizationSelect2View, ProblemSelect2View, TicketUserSelect2View, \
|
ContestUserSearchSelect2View, OrganizationSelect2View, ProblemSelect2View, TicketUserSelect2View, \
|
||||||
UserSearchSelect2View, UserSelect2View
|
UserSearchSelect2View, UserSelect2View
|
||||||
|
|
||||||
|
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
register_patterns = [
|
register_patterns = [
|
||||||
|
@ -368,8 +369,8 @@ urlpatterns = [
|
||||||
url(r'^custom_checker_sample/', about.custom_checker_sample, name='custom_checker_sample'),
|
url(r'^custom_checker_sample/', about.custom_checker_sample, name='custom_checker_sample'),
|
||||||
|
|
||||||
url(r'^chat/', include([
|
url(r'^chat/', include([
|
||||||
url(r'^$', ChatView.as_view(), name='chat'),
|
url(r'^$', login_required(ChatView.as_view()), name='chat'),
|
||||||
url(r'send$', send, name='send_message')
|
|
||||||
])),
|
])),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,7 @@ header {
|
||||||
|
|
||||||
#nav-shadow {
|
#nav-shadow {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: linear-gradient($widget_black, transparent);
|
background: linear-gradient(#63c8f9, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav-container {
|
#nav-container {
|
||||||
|
@ -281,7 +281,7 @@ nav {
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
|
||||||
&:link {
|
&:link {
|
||||||
color: #FFF;
|
color: #9c3706;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&: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 {
|
th {
|
||||||
height: 2em;
|
height: 2em;
|
||||||
color: #2b333b;
|
color: white;
|
||||||
background-color: $widget_black;
|
background-color: $widget_black;
|
||||||
border-color: #cccccc;
|
border-color: #cccccc;
|
||||||
border-width: 1px 1px 0 0;
|
border-width: 1px 1px 0 0;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
$highlight_blue: white;
|
$highlight_blue: white;
|
||||||
$widget_black: #63c8f9;//68BBE3, add8e6
|
$widget_black: #00007d;//68BBE3, add8e6
|
||||||
$border_gray: #ccc;
|
$border_gray: #ccc;
|
||||||
$background_gray: #ededed;
|
$background_gray: #ededed;
|
||||||
$background_light_gray: #fafafa;
|
$background_light_gray: #fafafa;
|
||||||
|
|
|
@ -419,7 +419,7 @@ ul.select2-selection__rendered {
|
||||||
margin: 0 -5px;
|
margin: 0 -5px;
|
||||||
background: $widget_black;
|
background: $widget_black;
|
||||||
border-radius: $widget_border_radius $widget_border_radius 0 0;
|
border-radius: $widget_border_radius $widget_border_radius 0 0;
|
||||||
color: #2b333b;
|
color: white;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
padding-left: 7px;
|
padding-left: 7px;
|
||||||
|
@ -427,7 +427,7 @@ ul.select2-selection__rendered {
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebox h3 .fa {
|
.sidebox h3 .fa {
|
||||||
color: #2b333b;
|
color: white;
|
||||||
float: right;
|
float: right;
|
||||||
margin: 0.2em 0.4em 0 0;
|
margin: 0.2em 0.4em 0 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block js_media %}
|
{% block js_media %}
|
||||||
|
<script src="{{ static('libs/jquery.waypoints.min.js')}}"></script>
|
||||||
|
<script src="{{ static('libs/infinite.min.js') }}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var chatSocket = new WebSocket(
|
var chatSocket = new WebSocket(
|
||||||
'ws://' + window.location.host +
|
'ws://' + window.location.host +
|
||||||
|
@ -12,15 +12,14 @@
|
||||||
chatSocket.onmessage = function(e) {
|
chatSocket.onmessage = function(e) {
|
||||||
let data = JSON.parse(e.data)
|
let data = JSON.parse(e.data)
|
||||||
data = data['message']
|
data = data['message']
|
||||||
console.log(data)
|
loadMessage(data['body'],
|
||||||
loadMessage(data['content'],
|
data['author'],
|
||||||
data['sender'],
|
data['time'],
|
||||||
data['time'],
|
|
||||||
data['image'])
|
data['image'])
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadMessage(content, user, time, image) {
|
function loadMessage(content, user, time, image) {
|
||||||
li = `<li>
|
li = `<li class="infinite-item">
|
||||||
<img src="${image}" class="profile-pic">
|
<img src="${image}" class="profile-pic">
|
||||||
<div class="body-message">
|
<div class="body-message">
|
||||||
<div class="user-time">
|
<div class="user-time">
|
||||||
|
@ -40,34 +39,44 @@
|
||||||
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
|
$('#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() {
|
$("#chat-submit").click(function() {
|
||||||
if ($("#chat-input").val().trim()) {
|
if ($("#chat-input").val().trim()) {
|
||||||
let content = $('#chat-input').val().trim();
|
let body = $('#chat-input').val().trim();
|
||||||
let img = '{{ gravatar(request.user, 32) }}'
|
let img = '{{ gravatar(request.user, 32) }}'
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
'content': content,
|
'body': body,
|
||||||
'image': img,
|
'image': img,
|
||||||
'time': calcTime(6), // HCM City
|
'author': '{{request.profile}}',
|
||||||
'sender': '{{ request.user }}'
|
'author_id': {{request.profile.id}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// $.post("send/", message)
|
||||||
chatSocket.send(JSON.stringify({
|
chatSocket.send(JSON.stringify({
|
||||||
'message': message,
|
'message': message
|
||||||
}))
|
}));
|
||||||
// $.post('/chat/send', message)
|
|
||||||
|
|
||||||
$('#chat-input').val('').focus();
|
$('#chat-input').val('').focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function calcTime(offset) {
|
|
||||||
utc = new Date().getTime()
|
|
||||||
nd = new Date(utc + (3600000*offset));
|
|
||||||
return nd.toLocaleString();
|
|
||||||
}
|
|
||||||
|
|
||||||
chatSocket.onclose = function(e) {
|
chatSocket.onclose = function(e) {
|
||||||
console.error('Chat socket closed unexpectedly');
|
console.error('Chat socket closed unexpectedly');
|
||||||
};
|
};
|
||||||
|
@ -108,29 +117,15 @@
|
||||||
{% endblock js_media %}
|
{% endblock js_media %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% csrf_token %}
|
|
||||||
<div id="chat-area">
|
<div id="chat-area">
|
||||||
<ul id="chat-log">
|
<ul id="chat-log" class="infinite-container">
|
||||||
<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>
|
</ul>
|
||||||
{{_('Your message')}}
|
{{_('Your message')}}
|
||||||
|
|
||||||
<textarea rows="6" id="chat-input"></textarea>
|
<textarea rows="6" id="chat-input"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<button id="chat-submit"> Send </button>
|
<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 %}
|
{% endblock body %}
|
||||||
{% endif %}
|
|
||||||
|
|
Loading…
Reference in a new issue