From cac44d5d366dabd87c42cc2ea38868f9bf777d1e Mon Sep 17 00:00:00 2001 From: Chris Fulljames Date: Fri, 23 May 2025 21:16:53 -0400 Subject: [PATCH] Add push notification settings --- src/littlesongplace/__init__.py | 11 -- src/littlesongplace/activity.py | 14 +- src/littlesongplace/push_notifications.py | 48 ++++- src/littlesongplace/sql/schema_update.sql | 1 + src/littlesongplace/static/nav.js | 13 +- src/littlesongplace/static/service.js | 23 --- src/littlesongplace/static/styles.css | 40 ++--- src/littlesongplace/templates/activity.html | 167 ++++++++++++++---- .../templates/comment-thread.html | 6 +- src/littlesongplace/templates/index.html | 4 +- 10 files changed, 209 insertions(+), 118 deletions(-) diff --git a/src/littlesongplace/__init__.py b/src/littlesongplace/__init__.py index 71727e1..846b83e 100644 --- a/src/littlesongplace/__init__.py +++ b/src/littlesongplace/__init__.py @@ -1,7 +1,6 @@ import base64 import logging import os -import random from datetime import datetime, timezone from logging.handlers import RotatingFileHandler from pathlib import Path @@ -56,15 +55,6 @@ def index(): for key, value in users.get_user_colors(user).items(): user[key] = value - titles = [ - ("Little Song Place", 2.0), - ("Lumpy Space Princess", 0.2), - ("Language Server Protocol", 0.1), - ("Liskov Substitution Principle", 0.1), - ] - titles, weights = zip(*titles) - title = random.choices(titles, weights)[0] - rows = db.query( """ SELECT * FROM jams @@ -81,7 +71,6 @@ def index(): "index.html", users=all_users, songs=page_songs, - page_title=title, ongoing_events=ongoing_events, upcoming_events=upcoming_events) diff --git a/src/littlesongplace/activity.py b/src/littlesongplace/activity.py index e3df8d4..eb1ca5d 100644 --- a/src/littlesongplace/activity.py +++ b/src/littlesongplace/activity.py @@ -2,7 +2,7 @@ from datetime import datetime, timezone from flask import Blueprint, redirect, render_template, session -from . import comments, db, songs +from . import comments, db, push_notifications, songs bp = Blueprint("activity", __name__) @@ -91,7 +91,17 @@ def activity(): [timestamp, session["userid"]]) db.commit() - return render_template("activity.html", comments=notifications) + comment_push = False + song_push = False + if "subid" in session: + row = db.query( + "SELECT settings FROM users_push_subscriptions WHERE subid = ?", + [session["subid"]], one=True) + if row: + comment_push = (row["settings"] & push_notifications.SubscriptionSetting.COMMENTS) > 0 + song_push = (row["settings"] & push_notifications.SubscriptionSetting.SONGS) > 0 + + return render_template("activity.html", comments=notifications, comment_push=comment_push, song_push=song_push) @bp.get("/new-activity") def new_activity(): diff --git a/src/littlesongplace/push_notifications.py b/src/littlesongplace/push_notifications.py index 1dd87c8..dde8900 100644 --- a/src/littlesongplace/push_notifications.py +++ b/src/littlesongplace/push_notifications.py @@ -1,13 +1,18 @@ import json import threading +import enum import pywebpush -from flask import Blueprint, current_app, g, request +from flask import Blueprint, current_app, g, request, session from . import auth, datadir, db bp = Blueprint("push-notifications", __name__, url_prefix="/push-notifications") +class SubscriptionSetting(enum.IntEnum): + COMMENTS = 0x0001 + SONGS = 0x0002 + @bp.post("/subscribe") @auth.requires_login def subscribe(): @@ -15,16 +20,49 @@ def subscribe(): # Request must contain valid subscription JSON abort(400) - db.query( + row = db.query( """ - INSERT INTO users_push_subscriptions (userid, subscription) - VALUES (?, ?) + INSERT INTO users_push_subscriptions (userid, subscription, settings) + VALUES (?, ?, ?) + RETURNING subid """, - [g.userid, json.dumps(request.json)]) + [g.userid, json.dumps(request.json), 0], expect_one=True) db.commit() current_app.logger.info(f"{g.username} registered push subscription") + session["subid"] = row["subid"] + + return {"status": "success"} + +@bp.post("/update-settings") +@auth.requires_login +def update_settings(): + if not request.json: + # Request must contain valid subscription JSON + abort(400) + + if "subid" not in session: + return {"status": "failed", "message": "no subid in current session"} + + bitfield = 0 + settings = request.json + if settings["comments"]: + bitfield |= SubscriptionSetting.COMMENTS + if settings["songs"]: + bitfield |= SubscriptionSetting.SONGS + + db.query( + """ + UPDATE users_push_subscriptions + SET settings = ? + WHERE subid = ? AND userid = ? + """, + [bitfield, session["subid"], g.userid]) + db.commit() + + current_app.logger.info(f"{g.username} updated push subscription settings: {bitfield:04x}") + return {"status": "success"} def get_user_subscriptions(userid): diff --git a/src/littlesongplace/sql/schema_update.sql b/src/littlesongplace/sql/schema_update.sql index 9b480f3..dde27a0 100644 --- a/src/littlesongplace/sql/schema_update.sql +++ b/src/littlesongplace/sql/schema_update.sql @@ -3,6 +3,7 @@ CREATE TABLE users_push_subscriptions ( subid INTEGER PRIMARY KEY, userid INTEGER NOT NULL, subscription TEXT NOT NULL, + settings INTEGER NOT NULL, FOREIGN KEY(userid) REFERENCES users(userid) ON DELETE CASCADE ); diff --git a/src/littlesongplace/static/nav.js b/src/littlesongplace/static/nav.js index dc89f3e..12736b4 100644 --- a/src/littlesongplace/static/nav.js +++ b/src/littlesongplace/static/nav.js @@ -39,18 +39,13 @@ document.addEventListener("DOMContentLoaded", async (e) => { let date = new Date(e.dataset.date); e.textContent = date.toLocaleString(); }); -}); -async function requestNotificationPermission() { - const permission = await window.Notification.requestPermission(); - if (permission === "granted") { - // Register service worker + // Register service worker + if ("serviceWorker" in navigator) + { navigator.serviceWorker.register("service.js"); } - else { - console.log("Did not get permission to send notifications:", permission); - } -} +}); function onLinkClick(event) { if (event.defaultPrevented) { diff --git a/src/littlesongplace/static/service.js b/src/littlesongplace/static/service.js index 17bee8d..30da09d 100644 --- a/src/littlesongplace/static/service.js +++ b/src/littlesongplace/static/service.js @@ -1,26 +1,3 @@ -const vapid_public_key = "BLsO37LostwqKch7SFr5Df0MexEoBOcujdMRY7wJurRPc_MGdz9rAkMrqs_dil4qSFxVbVyAA3FqLEPSL-WRNZs"; - -self.addEventListener("activate", async () => { - try { - // Subscribe via browser's push service - const options = {userVisibleOnly: true, applicationServerKey: vapid_public_key}; - const subscription = await self.registration.pushManager.subscribe(options); - console.log(JSON.stringify(subscription)); - - // Register subscription with LSP server - const response = await fetch( - "/push-notifications/subscribe", { - method: "post", - headers: {"Content-Type": "application/json"}, - body: JSON.stringify(subscription) - } - ); - console.log(response); - } - catch (err) { - console.log("Error while activating service:", err); - } -}); self.addEventListener("push", (event) => { if (event.data) { diff --git a/src/littlesongplace/static/styles.css b/src/littlesongplace/static/styles.css index 0836ceb..b446426 100644 --- a/src/littlesongplace/static/styles.css +++ b/src/littlesongplace/static/styles.css @@ -121,6 +121,22 @@ input[type=text], input[type=password], input[type=url], input[type=datetime-loc box-shadow: -2px -2px 0px 0px var(--black); } +.boxy { + margin: 10px 0px; + padding: 10px; + box-shadow: 2px 2px 0px 0px; + border: 1px solid; + border-radius: var(--radius); +} + +.boxy-lite { + margin: 10px 0px; + padding: 10px; + box-shadow: 1px 1px 0px 0px; + border: 1px solid; + border-radius: var(--radius); +} + select { border: none; background-color: var(--purple); @@ -468,21 +484,6 @@ div.song-details { margin: 10px; } -div.top-level-comment { - margin-top: 10px; - padding: 10px; - border: 2px solid; - border-radius: var(--radius); -} - -div.reply-comment { - margin-top: 10px; - margin-bottom: 10px; - padding: 10px; - border: 2px solid; - border-radius: var(--radius); -} - div.comment-button-container { display: flex; gap: 10px; @@ -617,15 +618,6 @@ div.player-info { font-size: 14px; } -/* Activity */ -div.comment-notification { - margin: 10px; - padding: 10px; - box-shadow: 2px 2px 0px 0px; - border: 1px solid; - border-radius: var(--radius); -} - /* Platform-specific global overrides */ @media screen and (max-width: 480px) { .desktop-only { diff --git a/src/littlesongplace/templates/activity.html b/src/littlesongplace/templates/activity.html index 19f8f25..d2a270a 100644 --- a/src/littlesongplace/templates/activity.html +++ b/src/littlesongplace/templates/activity.html @@ -3,49 +3,140 @@ {% block title %}Activity{% endblock %} {% block body %} - -{% if comments %}

activity

- {% for comment in comments %} -
- {{ comment['comment_username'] }} - {% if comment['replyto_content'] %} - replied to "{{ comment['replyto_content'] }}" - {% else %} - commented - {% endif %} - on - {% if 'songid' in comment %} - {{ comment['title'] }} - - {# Nothing to do for user profile #} - {% elif 'playlistid' in comment %} - {{ comment['name'] }} - - {% elif 'eventid' in comment %} - {{ comment['title'] }} - - {% endif %} - {{ comment['content_username'] }} -
- {{ comment['comment_username'] }}: - {{ comment['content'] }} - -
- {% if comment['replytoid'] %} - - Reply - {% else %} - - Reply - {% endif %} -
+ + + + +
+
+ + + +
+ {% for comment in comments -%} +
+ {{ comment['comment_username'] }} + {% if comment['replyto_content'] %} + replied to "{{ comment['replyto_content'] }}" + {% else %} + commented + {% endif %} + on + {% if 'songid' in comment %} + {{ comment['title'] }} - + {# Nothing to do for user profile #} + {% elif 'playlistid' in comment %} + {{ comment['name'] }} - + {% elif 'eventid' in comment %} + {{ comment['title'] }} - + {% endif %} + {{ comment['content_username'] }} +
+ {{ comment['comment_username'] }}: + {{ comment['content'] }} + +
+ {% if comment['replytoid'] %} + + Reply + {% else %} + + Reply + {% endif %}
+
{% endfor %} -{% else %} - - Nothing to show here yet! - -{% endif %} + {% if not comments %}

Nothing to show here yet!

{% endif %} +
{% endblock %} diff --git a/src/littlesongplace/templates/comment-thread.html b/src/littlesongplace/templates/comment-thread.html index 6ac737d..1231e09 100644 --- a/src/littlesongplace/templates/comment-thread.html +++ b/src/littlesongplace/templates/comment-thread.html @@ -5,7 +5,7 @@ {% endif %} {% for comment in comments %} -
+
{{ comment['username'] }}: {{ (comment['content'].replace("\n", "
"))|safe }} @@ -27,10 +27,10 @@ {% endif %} {% for reply in comment['replies'] %} -
+
{{ reply['username'] }}: - {{ reply['content'] }} + {{ (reply['content'].replace("\n", "
"))|safe }} {% if current_userid == reply['userid'] or current_userid == thread_userid %}
diff --git a/src/littlesongplace/templates/index.html b/src/littlesongplace/templates/index.html index 92824dd..bd9cc2a 100644 --- a/src/littlesongplace/templates/index.html +++ b/src/littlesongplace/templates/index.html @@ -1,11 +1,9 @@ {% extends "base.html" %} -{% block title %}{{ page_title }}{% endblock %} +{% block title %}Little Song Place{% endblock %} {% block body %} - -

hello!

🎶
-- 2.39.5