Skip to content

Commit e582fb2

Browse files
committed
Bare bones notification support
1 parent 5f2c919 commit e582fb2

File tree

11 files changed

+570
-6
lines changed

11 files changed

+570
-6
lines changed

apps/notifications/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""
2+
Notifications App
3+
4+
Push/SMS notifcations and management thereof
5+
"""
6+
7+
from flask import Blueprint
8+
9+
notifications = Blueprint("notifications", __name__)
10+
11+
from . import views # noqa

apps/notifications/views.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from apps.common import json_response
2+
from main import db
3+
from flask import render_template, request, current_app as app
4+
from flask_login import current_user, login_required
5+
6+
from . import notifications
7+
from models.web_push import public_key, WebPushTarget
8+
9+
10+
@notifications.route("/")
11+
@login_required
12+
def index():
13+
return render_template("notifications/index.html", public_key=public_key())
14+
15+
16+
@notifications.route("/register", methods=["POST"])
17+
@json_response
18+
@login_required
19+
def register():
20+
payload = request.json
21+
22+
target = WebPushTarget.query.filter_by(
23+
user=current_user, endpoint=payload["endpoint"]
24+
).first()
25+
26+
if target is None:
27+
app.logger.info("Creating new target")
28+
target = WebPushTarget(
29+
user=current_user,
30+
endpoint=payload["endpoint"],
31+
subscription_info=payload,
32+
expires=payload.get("expires", None),
33+
)
34+
35+
db.session.add(target)
36+
db.session.commit()
37+
else:
38+
app.logger.info("Using existing target")
39+
40+
return {
41+
"id": target.id,
42+
"user_id": target.user_id,
43+
}

css/notifications.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.state {
2+
display: none;
3+
}
4+
5+
.state.visible {
6+
display: block;
7+
}

gulpfile.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const main_js = (cb) => pump(jsBuild('main.js'), cb),
9393
volunteer_schedule_js = (cb) => pump(jsBuild('volunteer-schedule.js'), cb),
9494
arrivals_js = (cb) => pump(jsBuild('arrivals.js'), cb),
9595
serviceworker_js = (cb) => pump(jsBuild('serviceworker.js'), cb),
96+
notifications_js = (cb) => pump(jsBuild('notifications.js'), cb);
9697

9798

9899
function js(cb) {
@@ -102,7 +103,8 @@ function js(cb) {
102103
schedule_js,
103104
volunteer_schedule_js,
104105
arrivals_js,
105-
serviceworker_js
106+
serviceworker_js,
107+
notifications_js
106108
)(cb);
107109
}
108110

@@ -116,6 +118,7 @@ function css(cb) {
116118
'css/receipt.scss',
117119
'css/schedule.scss',
118120
'css/volunteer_schedule.scss',
121+
'css/notifications.scss',
119122
'css/flask-admin.scss',
120123
'css/dhtmlxscheduler_flat.css'
121124
]),

js/notifications.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
async function enableNotifications(event) {
2+
let vapid_key = document.querySelector("meta[name=vapid_key]").getAttribute("value");
3+
let worker = await navigator.serviceWorker.ready;
4+
let result = await worker.pushManager.subscribe({
5+
userVisibleOnly: true,
6+
applicationServerKey: vapid_key,
7+
});
8+
9+
let response = await fetch("/account/notifications/register", {
10+
method: "POST",
11+
headers: {
12+
"Content-Type": "application/json"
13+
},
14+
body: JSON.stringify(result.toJSON())
15+
});
16+
}
17+
18+
async function checkPermissions() {
19+
let worker = await navigator.serviceWorker.ready;
20+
21+
if ("pushManager" in worker) {
22+
let permissions = await worker.pushManager.permissionState();
23+
document.getElementById('notification-state').querySelectorAll('.state').forEach(el => { el.classList.add('visible') })
24+
document.getElementById(`notification-state-${permissions}`).classList.add('visible')
25+
} else {
26+
console.log("No push notification support.");
27+
}
28+
}
29+
30+
if ("serviceWorker" in navigator) {
31+
checkPermissions();
32+
document.getElementById("enable-notifications").addEventListener("click", enableNotifications);
33+
}

js/serviceworker.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,13 @@ registerRoute(
6969
],
7070
}),
7171
);
72+
73+
addEventListener("push", (event) => {
74+
console.log("Push event received", event);
75+
const message = event.data.text()
76+
self.registration.showNotification(message);
77+
});
78+
79+
addEventListener("notificationclick", (event) => {
80+
self.clients.openWindow("http://localhost:2345");
81+
});

main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ def shell_imports():
314314
from apps.volunteer import volunteer
315315
from apps.volunteer.admin import volunteer_admin
316316
from apps.volunteer.admin.notify import notify
317+
from apps.notifications import notifications
317318

318319
app.register_blueprint(base)
319320
app.register_blueprint(users)
@@ -329,6 +330,7 @@ def shell_imports():
329330
app.register_blueprint(admin, url_prefix="/admin")
330331
app.register_blueprint(volunteer, url_prefix="/volunteer")
331332
app.register_blueprint(notify, url_prefix="/volunteer/admin/notify")
333+
app.register_blueprint(notifications, url_prefix="/account/notifications")
332334

333335
volunteer_admin.init_app(app)
334336

models/web_push.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
from datetime import datetime
22
from main import db
3+
from flask import current_app as app
4+
from pywebpush import webpush
35

46
from . import BaseModel
57

68

9+
def public_key():
10+
return app.config["WEBPUSH_PUBLIC_KEY"]
11+
12+
13+
def notify(target, message):
14+
webpush(
15+
subscription_info=target.subscription_info,
16+
data=message,
17+
vapid_private_key=app.config["WEBPUSH_PRIVATE_KEY"],
18+
vapid_claims={
19+
"sub": "mailto:[email protected]",
20+
},
21+
)
22+
23+
724
class WebPushTarget(BaseModel):
825
__table_name__ = "web_push_target"
926
id = db.Column(db.Integer, primary_key=True)

0 commit comments

Comments
 (0)