'content': self.message,
'senderId': self.user and self.user.id or "system",
'username': self.user and self.user.username or "Système",
- 'date': '13 November',
- 'timestamp': '10:20',
'date': self.created.strftime('%d/%m/%Y'),
'timestamp': self.created.strftime('%H:%M'),
'system': self.system
--- /dev/null
+
+from datetime import datetime, timedelta
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.db import models
+from django.urls.base import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from ..models import MetaCore
+from ..models.core import app_label
+from channels.layers import get_channel_layer
+from asgiref.sync import async_to_sync
+
+def in_one_week():
+ return datetime.utcnow() + timedelta(days=1)
+
+class Notification(models.Model):
+ """A notification message"""
+
+ message = models.CharField('Message', max_length=255)
+ url = models.CharField('Url', max_length=255)
+ user = models.ForeignKey(
+ User, related_name='notifications', verbose_name=_('user'), on_delete=models.CASCADE)
+ created = models.DateTimeField('Creation date', auto_now_add=True)
+ expired = models.DateTimeField("Date d'expiration", default=in_one_week)
+
+ class Meta(MetaCore):
+ db_table = app_label + '_' + 'notifications'
+
+ def __str__(self):
+ return f"{str(self.user)} - {self.message[:20]}"
+
+ @classmethod
+ def create(cls, user, room_name, message, system=False):
+ notification = Notification(user=user, message=message,
+ room_name=room_name, system=system)
+ notification.save()
+ return notification.to_dict()
+
+ def broadcast(self):
+ """
+ broadcast a notification to socket
+ """
+ channel_layer = get_channel_layer()
+ async_to_sync(channel_layer.send)(f"usernotification_{self.user.id}", {
+ 'type': 'notification',
+ 'message': self.to_dict()
+ })
+
+ def to_dict(self):
+ return {
+ '_id': self.id,
+ 'content': self.message,
+ 'url': self.url,
+ 'date': self.created.strftime('%d/%m/%Y'),
+ }
--- /dev/null
+<template>
+ Notification
+</template>
+
+<script lang="ts">
+import { Component, Vue } from "vue-property-decorator"
+
+@Component({
+})
+export default class Notification extends Vue {
+ socket: WebSocket | null = null
+ messagesLoaded = false
+ messages: Messages = []
+ currentUserId: number | null = null
+
+ created() {
+ // get room info
+ const userId = document.getElementById("user_id")
+ this.connect(roomInfo.room_name)
+ this.currentUserId = userId
+ }
+
+ connect(roomName: string) {
+ // connect to socket
+ let protocol = "wss"
+ if (window.location.protocol != "https:") protocol = "ws"
+ this.socket = new WebSocket(protocol + "://" + window.location.host + "/ws/notification/" + this.currentUserId + "/")
+ this.fetchMessages()
+
+ this.socket.onclose = () => {
+ console.log("Chat socket closed")
+ }
+ }
+
+ async fetchMessages() {
+ this.socket!.onmessage = (e) => {
+ const data = JSON.parse(e.data)
+ const type = data.type
+ // do not load messages in case of socket reconnect
+ if (type == "initial" && this.messagesLoaded) return
+ const newMessages = data.messages as Message[]
+ this.messages = [...this.messages, ...newMessages]
+ if (type == "initial") this.messagesLoaded = true
+ }
+ }
+}
+</script>
+
+<style>
+.vac-message-wrapper .vac-offset-current {
+ margin-left: 15%;
+}
+.vac-message-wrapper .vac-message-box {
+ max-width: 80%;
+}
+</style>
import "./compatibility"
import Vue from "vue"
import Chat from "./components/Chat.vue"
+import Notification from "./components/Notification.vue"
Vue.config.productionTip = false
render: (h) => h(Chat)
}).$mount("#chat")
}
+
+
+if (document.getElementById("notification")) {
+ new Vue({
+ render: (h) => h(Notification)
+ }).$mount("#notification")
+}
</ul>
{% endblock menu %}
</div>
+
+
+ <div id="notifications"></div>
</div>
{% endblock header %}
--- /dev/null
+from teleforma.ws.logger import log_consumer_exceptions
+from teleforma.models.notification import Notification
+from django.conf import settings
+from channels.generic.websocket import AsyncJsonWebsocketConsumer
+from channels.db import database_sync_to_async
+
+
+@log_consumer_exceptions
+class NotificationConsumer(AsyncJsonWebsocketConsumer):
+
+ async def connect(self):
+ await self.accept()
+
+ # send initial messages
+ data = await self.get_notifications_list()
+ await self.send_json(content={'type': 'initial', 'messages': data})
+
+ @database_sync_to_async
+ def get_notifications_list(self):
+ messages = [message.to_dict() for message in Notification.objects.filter(
+ user=self.user).order_by('-created')]
+ return messages
+
+ # Receive message from channel
+ async def notification_message(self, event):
+ message = event['message']
+ # Send message to WebSocket
+ await self.send_json(content={'type': 'new', 'messages': [message]})
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import re_path
+
from . import chat
+from . import notification
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'teleforma.settings')
sys.path.append(os.path.dirname('.'))
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', chat.ChatConsumer.as_asgi()),
+ re_path(r'ws/notification/(?P<user_id>\w+)/$', notification.NotificationConsumer.as_asgi()),
]