Skip to content

Commit

Permalink
Bare bones notification support
Browse files Browse the repository at this point in the history
  • Loading branch information
jellybob committed Apr 1, 2024
1 parent 5f2c919 commit e582fb2
Show file tree
Hide file tree
Showing 11 changed files with 570 additions and 6 deletions.
11 changes: 11 additions & 0 deletions apps/notifications/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
Notifications App
Push/SMS notifcations and management thereof
"""

from flask import Blueprint

notifications = Blueprint("notifications", __name__)

from . import views # noqa
43 changes: 43 additions & 0 deletions apps/notifications/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from apps.common import json_response
from main import db
from flask import render_template, request, current_app as app
from flask_login import current_user, login_required

from . import notifications
from models.web_push import public_key, WebPushTarget


@notifications.route("/")
@login_required
def index():
return render_template("notifications/index.html", public_key=public_key())


@notifications.route("/register", methods=["POST"])
@json_response
@login_required
def register():
payload = request.json

target = WebPushTarget.query.filter_by(
user=current_user, endpoint=payload["endpoint"]
).first()

if target is None:
app.logger.info("Creating new target")
target = WebPushTarget(
user=current_user,
endpoint=payload["endpoint"],
subscription_info=payload,
expires=payload.get("expires", None),
)

db.session.add(target)
db.session.commit()
else:
app.logger.info("Using existing target")

return {
"id": target.id,
"user_id": target.user_id,
}
7 changes: 7 additions & 0 deletions css/notifications.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.state {
display: none;
}

.state.visible {
display: block;
}
5 changes: 4 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const main_js = (cb) => pump(jsBuild('main.js'), cb),
volunteer_schedule_js = (cb) => pump(jsBuild('volunteer-schedule.js'), cb),
arrivals_js = (cb) => pump(jsBuild('arrivals.js'), cb),
serviceworker_js = (cb) => pump(jsBuild('serviceworker.js'), cb),
notifications_js = (cb) => pump(jsBuild('notifications.js'), cb);


function js(cb) {
Expand All @@ -102,7 +103,8 @@ function js(cb) {
schedule_js,
volunteer_schedule_js,
arrivals_js,
serviceworker_js
serviceworker_js,
notifications_js
)(cb);
}

Expand All @@ -116,6 +118,7 @@ function css(cb) {
'css/receipt.scss',
'css/schedule.scss',
'css/volunteer_schedule.scss',
'css/notifications.scss',
'css/flask-admin.scss',
'css/dhtmlxscheduler_flat.css'
]),
Expand Down
33 changes: 33 additions & 0 deletions js/notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
async function enableNotifications(event) {
let vapid_key = document.querySelector("meta[name=vapid_key]").getAttribute("value");
let worker = await navigator.serviceWorker.ready;
let result = await worker.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: vapid_key,
});

let response = await fetch("/account/notifications/register", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(result.toJSON())
});
}

async function checkPermissions() {
let worker = await navigator.serviceWorker.ready;

if ("pushManager" in worker) {
let permissions = await worker.pushManager.permissionState();
document.getElementById('notification-state').querySelectorAll('.state').forEach(el => { el.classList.add('visible') })
document.getElementById(`notification-state-${permissions}`).classList.add('visible')
} else {
console.log("No push notification support.");
}
}

if ("serviceWorker" in navigator) {
checkPermissions();
document.getElementById("enable-notifications").addEventListener("click", enableNotifications);
}
10 changes: 10 additions & 0 deletions js/serviceworker.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,13 @@ registerRoute(
],
}),
);

addEventListener("push", (event) => {
console.log("Push event received", event);
const message = event.data.text()
self.registration.showNotification(message);
});

addEventListener("notificationclick", (event) => {
self.clients.openWindow("http://localhost:2345");
});
2 changes: 2 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ def shell_imports():
from apps.volunteer import volunteer
from apps.volunteer.admin import volunteer_admin
from apps.volunteer.admin.notify import notify
from apps.notifications import notifications

app.register_blueprint(base)
app.register_blueprint(users)
Expand All @@ -329,6 +330,7 @@ def shell_imports():
app.register_blueprint(admin, url_prefix="/admin")
app.register_blueprint(volunteer, url_prefix="/volunteer")
app.register_blueprint(notify, url_prefix="/volunteer/admin/notify")
app.register_blueprint(notifications, url_prefix="/account/notifications")

volunteer_admin.init_app(app)

Expand Down
17 changes: 17 additions & 0 deletions models/web_push.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
from datetime import datetime
from main import db
from flask import current_app as app
from pywebpush import webpush

from . import BaseModel


def public_key():
return app.config["WEBPUSH_PUBLIC_KEY"]


def notify(target, message):
webpush(
subscription_info=target.subscription_info,
data=message,
vapid_private_key=app.config["WEBPUSH_PRIVATE_KEY"],
vapid_claims={
"sub": "mailto:[email protected]",
},
)


class WebPushTarget(BaseModel):
__table_name__ = "web_push_target"
id = db.Column(db.Integer, primary_key=True)
Expand Down
Loading

0 comments on commit e582fb2

Please sign in to comment.