From ad451c2ae0b0983febb0cbde3f47eac0979e3b58 Mon Sep 17 00:00:00 2001 From: SDE Date: Mon, 8 Jan 2024 14:54:59 +0300 Subject: [PATCH] 0.10.0 browser push messages --- GeneralApp/funcs.py | 26 ++++++++- GeneralApp/views.py | 2 + PushMessages/__init__.py | 0 PushMessages/admin.py | 3 + PushMessages/apps.py | 6 ++ PushMessages/migrations/__init__.py | 0 PushMessages/models.py | 3 + PushMessages/tests.py | 3 + PushMessages/urls.py | 16 +++++ PushMessages/views.py | 39 +++++++++++++ TWB/urls.py | 2 + requirements.pip | 1 + static/js/push/registerSw.js | 91 +++++++++++++++++++++++++++++ templates/sw.js | 18 ++++++ 14 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 PushMessages/__init__.py create mode 100644 PushMessages/admin.py create mode 100644 PushMessages/apps.py create mode 100644 PushMessages/migrations/__init__.py create mode 100644 PushMessages/models.py create mode 100644 PushMessages/tests.py create mode 100644 PushMessages/urls.py create mode 100644 PushMessages/views.py create mode 100644 static/js/push/registerSw.js create mode 100644 templates/sw.js diff --git a/GeneralApp/funcs.py b/GeneralApp/funcs.py index 983fc4b..815796f 100644 --- a/GeneralApp/funcs.py +++ b/GeneralApp/funcs.py @@ -1,14 +1,38 @@ from django.http import HttpResponse, Http404, FileResponse +from django.conf import settings def get_inter_Dict(user): from SubscribesApp.funcs import get_cur_user_subscribe user_subscribe = get_cur_user_subscribe(user) - return {'user_subscribe': user_subscribe} + Dict = { + 'user_subscribe': user_subscribe, + } + from PushMessages.views import get_key_Dict + Dict.update(get_key_Dict()) + + return Dict def get_inter_http_respose(template_obj, context_Dict, request): context_Dict.update(get_inter_Dict(request.user)) + from PushMessages.views import send_push + if request and 'page' in context_Dict: + text = None + title = None + + if context_Dict['page'] == dict: + if 'title' in context_Dict['page']: + title = context_Dict['page']['title'] + if 'description' in context_Dict['page']: + text = context_Dict['page']['description'] + else: + title = getattr(context_Dict['page'], 'title', None) + text = getattr(context_Dict['page'], 'description', None) + + if text and title and not request.user.is_anonymous: + send_push(user=request.user, title=title, text=text) + return HttpResponse(template_obj.render(context_Dict, request)) \ No newline at end of file diff --git a/GeneralApp/views.py b/GeneralApp/views.py index 16bf7a6..9af48ce 100644 --- a/GeneralApp/views.py +++ b/GeneralApp/views.py @@ -89,6 +89,8 @@ def MainPage(request): 'owner_type': 'mover' } + + breadcrumbs_Dict = { } Dict.update({'breadcrumbs': breadcrumbs_Dict}) diff --git a/PushMessages/__init__.py b/PushMessages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PushMessages/admin.py b/PushMessages/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/PushMessages/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/PushMessages/apps.py b/PushMessages/apps.py new file mode 100644 index 0000000..7034e70 --- /dev/null +++ b/PushMessages/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PushmessagesConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'PushMessages' diff --git a/PushMessages/migrations/__init__.py b/PushMessages/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PushMessages/models.py b/PushMessages/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/PushMessages/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/PushMessages/tests.py b/PushMessages/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/PushMessages/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/PushMessages/urls.py b/PushMessages/urls.py new file mode 100644 index 0000000..961d48c --- /dev/null +++ b/PushMessages/urls.py @@ -0,0 +1,16 @@ +# coding=utf-8 +from django.urls import path, include +# from AuthApp.js_views import * +# from AuthApp.import_funcs import * +from .views import * +from django.contrib.auth import views +from RoutesApp.js_views import new_route_view_ajax +from django.views.generic import TemplateView + +urlpatterns = [ + + path('send_push', send_push), + path('webpush/', include('webpush.urls')), + path('sw.js', TemplateView.as_view(template_name='sw.js', content_type='application/x-javascript')), + +] \ No newline at end of file diff --git a/PushMessages/views.py b/PushMessages/views.py new file mode 100644 index 0000000..39889d2 --- /dev/null +++ b/PushMessages/views.py @@ -0,0 +1,39 @@ +from django.http.response import JsonResponse, HttpResponse +from django.views.decorators.http import require_GET, require_POST +from django.contrib.auth.models import User +from django.views.decorators.csrf import csrf_exempt +from webpush import send_user_notification +import json +from django.shortcuts import render, get_object_or_404 +from django.conf import settings + + +def get_key_Dict(): + webpush_settings = getattr(settings, 'WEBPUSH_SETTINGS', {}) + vapid_key = webpush_settings.get('VAPID_PUBLIC_KEY') + Dict = { + 'vapid_key': vapid_key + } + return Dict + +def send_push(user, title, text, img=None): + try: + # body = request.body + # data = json.loads(body) + # + # if 'head' not in data or 'body' not in data or 'id' not in data: + # return JsonResponse(status=400, data={"message": "Invalid data format"}) + # + # user_id = data['id'] + # user = get_object_or_404(User, pk=user_id) + Dict = { + 'head': title, + 'body': text + } + + # payload = {'head': data['head'], 'body': data['body']} + send_user_notification(user=user, payload=Dict, ttl=1000) + + return JsonResponse(status=200, data={"message": "Web push successful"}) + except TypeError: + return JsonResponse(status=500, data={"message": "An error occurred"}) diff --git a/TWB/urls.py b/TWB/urls.py index 6611292..c772353 100644 --- a/TWB/urls.py +++ b/TWB/urls.py @@ -29,6 +29,8 @@ urlpatterns = [ path('', include('ArticlesApp.js_urls')), path('test_404', Page404, name='page_404'), + + path('', include('PushMessages.urls')) ] from django.conf.urls.i18n import i18n_patterns diff --git a/requirements.pip b/requirements.pip index abb2b5c..618a0c6 100644 --- a/requirements.pip +++ b/requirements.pip @@ -10,4 +10,5 @@ channels==4.0.0 daphne==4.0.0 channels-redis==4.1.0 django-colorfield +django-webpush==0.3.5 diff --git a/static/js/push/registerSw.js b/static/js/push/registerSw.js new file mode 100644 index 0000000..1220512 --- /dev/null +++ b/static/js/push/registerSw.js @@ -0,0 +1,91 @@ +const registerSw = async () => { + if ('serviceWorker' in navigator) { + const reg = await navigator.serviceWorker.register('/sw.js'); + initialiseState(reg) + + } else { + showNotAllowed("You can't send push notifications ☹️😒") + } +}; + +const initialiseState = (reg) => { + if (!reg.showNotification) { + showNotAllowed('Showing notifications isn\'t supported ☹️😒'); + return + } + if (Notification.permission === 'denied') { + showNotAllowed('You prevented us from showing notifications β˜ΉοΈπŸ€”'); + return + } + if (!'PushManager' in window) { + showNotAllowed("Push isn't allowed in your browser πŸ€”"); + return + } + subscribe(reg); +} + +const showNotAllowed = (message) => { + const button = document.querySelector('form>button'); + button.innerHTML = `${message}`; + button.setAttribute('disabled', 'true'); +}; + +function urlB64ToUint8Array(base64String) { + const padding = '='.repeat((4 - base64String.length % 4) % 4); + const base64 = (base64String + padding) + .replace(/\-/g, '+') + .replace(/_/g, '/'); + + const rawData = window.atob(base64); + const outputArray = new Uint8Array(rawData.length); + const outputData = outputArray.map((output, index) => rawData.charCodeAt(index)); + + return outputData; +} + +const subscribe = async (reg) => { + const subscription = await reg.pushManager.getSubscription(); + if (subscription) { + sendSubData(subscription); + return; + } + + const vapidMeta = document.querySelector('meta[name="vapid-key"]'); + const key = vapidMeta.content; + const options = { + userVisibleOnly: true, + // if key exists, create applicationServerKey property + ...(key && {applicationServerKey: urlB64ToUint8Array(key)}) + }; + + const sub = await reg.pushManager.subscribe(options); + sendSubData(sub) +}; + +const sendSubData = async (subscription) => { + const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase(); + const data = { + status_type: 'subscribe', + subscription: subscription.toJSON(), + browser: browser, + user_agent: browser, + }; + + const res = await fetch('/webpush/save_information', { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'content-type': 'application/json', + + }, + credentials: "include" + }); + + handleResponse(res); +}; + +const handleResponse = (res) => { + console.log(res.status); +}; + +registerSw(); \ No newline at end of file diff --git a/templates/sw.js b/templates/sw.js new file mode 100644 index 0000000..db390a8 --- /dev/null +++ b/templates/sw.js @@ -0,0 +1,18 @@ +// Register event listener for the 'push' event. +self.addEventListener('push', function (event) { + // Retrieve the textual payload from event.data (a PushMessageData object). + // Other formats are supported (ArrayBuffer, Blob, JSON), check out the documentation + // on https://developer.mozilla.org/en-US/docs/Web/API/PushMessageData. + const eventInfo = event.data.text(); + const data = JSON.parse(eventInfo); + const head = data.head || 'New Notification πŸ•ΊπŸ•Ί'; + const body = data.body || 'This is default content. Your notification didn\'t have one πŸ™„πŸ™„'; + + // Keep the service worker alive until the notification is created. + event.waitUntil( + self.registration.showNotification(head, { + body: body, + icon: 'static/img/svg/Logo.svg' + }) + ); +}); \ No newline at end of file